aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js/apps/create/organization
diff options
context:
space:
mode:
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>2018-11-05 13:49:31 +0100
committerSonarTech <sonartech@sonarsource.com>2018-11-16 20:21:06 +0100
commit4e72416a414f4651cf9e0347b161c9be74b9782a (patch)
tree07b0e3fd321ff56edef82489f47f18231c1cab47 /server/sonar-web/src/main/js/apps/create/organization
parent3112801fbe5c8c36e15f52051162d2b6e0828812 (diff)
downloadsonarqube-4e72416a414f4651cf9e0347b161c9be74b9782a.tar.gz
sonarqube-4e72416a414f4651cf9e0347b161c9be74b9782a.zip
SONAR-11321 Apply feedback
* Do not autofocus when a default org is selected * Do not skip onboarding when opening the organization create page * Add button to cancel org import * Fix bug of org created with description in place of avatar * Redirect to organization projects after multiple projects import * Correctly select newly create organization when redirected to project creation page * Remove tutorial steps in auto import organization components * Update already imported repository link * Hide key value in the additional info when read only * Hide org type icons in the organization select of the page to manually create a project * Update wording to analyze projects instead of create projects * Display spinner while importing organization * Disable auto import of org for now when the user must create a paid org * Add placeholder to avatar input when there is no url specified * Add missing app installation text in create project page * Allow to switch between tabs during organization import and keep data * Remove read-only key when binding personal org
Diffstat (limited to 'server/sonar-web/src/main/js/apps/create/organization')
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationBind.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationCreate.tsx188
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/AutoPersonalOrganizationBind.tsx71
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/CreateOrganization.tsx102
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/ManualOrganizationCreate.tsx7
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/OrganizationDetailsForm.tsx39
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/PlanStep.tsx5
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/RemoteOrganizationChoose.tsx (renamed from server/sonar-web/src/main/js/apps/create/organization/ChooseRemoteOrganizationStep.tsx)30
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoOrganizationCreate-test.tsx15
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoPersonalOrganizationBind-test.tsx18
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/__tests__/CreateOrganization-test.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/__tests__/RemoteOrganizationChoose-test.tsx (renamed from server/sonar-web/src/main/js/apps/create/organization/__tests__/ChooseRemoteOrganizationStep-test.tsx)8
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/AutoOrganizationBind-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/AutoOrganizationCreate-test.tsx.snap237
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/AutoPersonalOrganizationBind-test.tsx.snap96
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/ChooseRemoteOrganizationStep-test.tsx.snap222
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/CreateOrganization-test.tsx.snap87
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/ManualOrganizationCreate-test.tsx.snap8
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/OrganizationDetailsForm-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/RemoteOrganizationChoose-test.tsx.snap182
20 files changed, 698 insertions, 633 deletions
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
index 9511f639648..f8275990c8d 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationBind.tsx
+++ b/server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationBind.tsx
@@ -18,8 +18,9 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { Organization } from '../../../app/types';
+import DeferredSpinner from '../../../components/common/DeferredSpinner';
import OrganizationSelect from '../components/OrganizationSelect';
+import { Organization } from '../../../app/types';
import { SubmitButton } from '../../../components/ui/buttons';
import { translate } from '../../../helpers/l10n';
@@ -84,10 +85,11 @@ export default class AutoOrganizationBind extends React.PureComponent<Props, Sta
organization={organization}
organizations={this.props.unboundOrganizations}
/>
- <div className="big-spacer-top">
+ <div className="display-flex-center big-spacer-top">
<SubmitButton disabled={submitting || !organization}>
{translate('onboarding.import_organization.bind')}
</SubmitButton>
+ {submitting && <DeferredSpinner className="spacer-left" />}
</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 5adbae0b71c..178e1e12d41 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
@@ -18,12 +18,14 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
+import * as classNames from 'classnames';
import { FormattedMessage } from 'react-intl';
import AutoOrganizationBind from './AutoOrganizationBind';
-import ChooseRemoteOrganizationStep from './ChooseRemoteOrganizationStep';
+import RemoteOrganizationChoose from './RemoteOrganizationChoose';
import OrganizationDetailsForm from './OrganizationDetailsForm';
-import OrganizationDetailsStep from './OrganizationDetailsStep';
+import { Query } from './utils';
import RadioToggle from '../../../components/controls/RadioToggle';
+import { DeleteButton } from '../../../components/ui/buttons';
import {
AlmApplication,
AlmOrganization,
@@ -47,11 +49,13 @@ interface Props {
almOrganization?: AlmOrganization;
almUnboundApplications: AlmUnboundApplication[];
boundOrganization?: OrganizationBase;
+ className?: string;
createOrganization: (
organization: OrganizationBase & { installationId?: string }
) => Promise<Organization>;
onOrgCreated: (organization: string, justCreated?: boolean) => void;
unboundOrganizations: Organization[];
+ updateUrlQuery: (query: Partial<Query>) => void;
}
interface State {
@@ -66,8 +70,18 @@ export default class AutoOrganizationCreate extends React.PureComponent<Props, S
};
}
- handleOptionChange = (filter: Filters) => {
- this.setState({ filter });
+ handleBindOrganization = (organization: string) => {
+ if (this.props.almInstallId) {
+ return bindAlmOrganization({
+ organization,
+ installationId: this.props.almInstallId
+ }).then(() => this.props.onOrgCreated(organization, false));
+ }
+ return Promise.reject();
+ };
+
+ handleCancelImport = () => {
+ this.props.updateUrlQuery({ almInstallId: undefined, almKey: undefined });
};
handleCreateOrganization = (organization: Required<OrganizationBase>) => {
@@ -83,98 +97,96 @@ export default class AutoOrganizationCreate extends React.PureComponent<Props, S
.then(({ key }) => this.props.onOrgCreated(key));
};
- handleBindOrganization = (organization: string) => {
- if (this.props.almInstallId) {
- return bindAlmOrganization({
- organization,
- installationId: this.props.almInstallId
- }).then(() => this.props.onOrgCreated(organization, false));
- }
- return Promise.reject();
+ handleOptionChange = (filter: Filters) => {
+ this.setState({ filter });
};
- render() {
- const {
- almApplication,
- almInstallId,
- almOrganization,
- boundOrganization,
- unboundOrganizations
- } = this.props;
- if (almInstallId && almOrganization && !boundOrganization) {
- const { filter } = this.state;
- const hasUnboundOrgs = unboundOrganizations.length > 0;
- return (
- <OrganizationDetailsStep
- finished={false}
- onOpen={() => {}}
- open={true}
- organization={almOrganization}>
- <div className="huge-spacer-bottom">
- <p className="big-spacer-bottom">
- <FormattedMessage
- defaultMessage={translate('onboarding.import_organization_x')}
- id="onboarding.import_organization_x"
- values={{
- avatar: (
- <img
- alt={almApplication.name}
- className="little-spacer-left"
- src={`${getBaseUrl()}/images/sonarcloud/${sanitizeAlmId(
- almApplication.key
- )}.svg`}
- width={16}
- />
- ),
- name: <strong>{almOrganization.name}</strong>
- }}
- />
- </p>
-
- {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>
+ renderContent = (almOrganization: AlmOrganization) => {
+ const { almApplication, unboundOrganizations } = this.props;
- {filter === Filters.Create && (
- <OrganizationDetailsForm
- onContinue={this.handleCreateOrganization}
- organization={almOrganization}
- submitText={translate('onboarding.import_organization.import')}
+ const { filter } = this.state;
+ const hasUnboundOrgs = unboundOrganizations.length > 0;
+ return (
+ <div className="boxed-group-inner">
+ <div className="huge-spacer-bottom">
+ <p className="display-flex-center big-spacer-bottom">
+ <FormattedMessage
+ defaultMessage={translate('onboarding.import_organization_x')}
+ id="onboarding.import_organization_x"
+ values={{
+ avatar: (
+ <img
+ alt={almApplication.name}
+ className="little-spacer-left"
+ src={`${getBaseUrl()}/images/sonarcloud/${sanitizeAlmId(
+ almApplication.key
+ )}.svg`}
+ width={16}
+ />
+ ),
+ name: <strong>{almOrganization.name}</strong>
+ }}
/>
- )}
- {filter === Filters.Bind && (
- <AutoOrganizationBind
- onBindOrganization={this.handleBindOrganization}
- unboundOrganizations={unboundOrganizations}
+ <DeleteButton className="little-spacer-left" onClick={this.handleCancelImport} />
+ </p>
+
+ {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}
/>
)}
- </OrganizationDetailsStep>
- );
- }
+ </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}
+ />
+ )}
+ </div>
+ );
+ };
+
+ render() {
+ const { almInstallId, almOrganization, boundOrganization, className } = this.props;
return (
- <ChooseRemoteOrganizationStep
- almApplication={this.props.almApplication}
- almInstallId={almInstallId}
- almOrganization={almOrganization}
- almUnboundApplications={this.props.almUnboundApplications}
- boundOrganization={boundOrganization}
- />
+ <div className={classNames('boxed-group', className)}>
+ <div className="boxed-group-header">
+ <h2>{translate('onboarding.import_organization.import_org_details')}</h2>
+ </div>
+
+ {almInstallId && almOrganization && !boundOrganization ? (
+ this.renderContent(almOrganization)
+ ) : (
+ <RemoteOrganizationChoose
+ almApplication={this.props.almApplication}
+ almInstallId={almInstallId}
+ almOrganization={almOrganization}
+ almUnboundApplications={this.props.almUnboundApplications}
+ boundOrganization={boundOrganization}
+ />
+ )}
+ </div>
);
}
}
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
index 5524c1fc19a..5191bbee8be 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/AutoPersonalOrganizationBind.tsx
+++ b/server/sonar-web/src/main/js/apps/create/organization/AutoPersonalOrganizationBind.tsx
@@ -20,7 +20,8 @@
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import OrganizationDetailsForm from './OrganizationDetailsForm';
-import OrganizationDetailsStep from './OrganizationDetailsStep';
+import { Query } from './utils';
+import { DeleteButton } from '../../../components/ui/buttons';
import {
AlmApplication,
AlmOrganization,
@@ -41,9 +42,14 @@ interface Props {
updateOrganization: (
organization: OrganizationBase & { installationId?: string }
) => Promise<Organization>;
+ updateUrlQuery: (query: Partial<Query>) => void;
}
export default class AutoPersonalOrganizationBind extends React.PureComponent<Props> {
+ handleCancelImport = () => {
+ this.props.updateUrlQuery({ almInstallId: undefined, almKey: undefined });
+ };
+
handleCreateOrganization = (organization: Required<OrganizationBase>) => {
return this.props
.updateOrganization({
@@ -60,39 +66,40 @@ export default class AutoPersonalOrganizationBind extends React.PureComponent<Pr
render() {
const { almApplication, importPersonalOrg } = this.props;
return (
- <OrganizationDetailsStep
- finished={false}
- onOpen={() => {}}
- open={true}
- organization={importPersonalOrg}>
- <div 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>
- }}
+ <div className="boxed-group">
+ <div className="boxed-group-inner">
+ <div className="display-flex-center big-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>
+ }}
+ />
+ <DeleteButton className="little-spacer-left" onClick={this.handleCancelImport} />
+ </div>
+ <OrganizationDetailsForm
+ keyReadOnly={true}
+ onContinue={this.handleCreateOrganization}
+ organization={importPersonalOrg}
+ submitText={translate('onboarding.import_organization.bind')}
/>
</div>
- <OrganizationDetailsForm
- keyReadOnly={true}
- onContinue={this.handleCreateOrganization}
- organization={importPersonalOrg}
- submitText={translate('onboarding.import_organization.bind')}
- />
- </OrganizationDetailsStep>
+ </div>
);
}
}
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 c12db4930b9..9e54bf6295b 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,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
+import * as classNames from 'classnames';
import { differenceInMinutes } from 'date-fns';
import { times } from 'lodash';
import { connect } from 'react-redux';
@@ -27,8 +28,10 @@ import { FormattedMessage } from 'react-intl';
import { Link, withRouter, WithRouterProps } from 'react-router';
import {
formatPrice,
+ ORGANIZATION_IMPORT_REDIRECT_TO_PROJECT_TIMESTAMP,
parseQuery,
- ORGANIZATION_IMPORT_REDIRECT_TO_PROJECT_TIMESTAMP
+ serializeQuery,
+ Query
} from './utils';
import AlmApplicationInstalling from './AlmApplicationInstalling';
import AutoOrganizationCreate from './AutoOrganizationCreate';
@@ -90,6 +93,8 @@ interface State {
subscriptionPlans?: SubscriptionPlan[];
}
+type StateWithAutoImport = State & Required<Pick<State, 'almApplication'>>;
+
type TabKeys = 'auto' | 'manual';
interface LocationState {
@@ -158,6 +163,10 @@ export class CreateOrganization extends React.PureComponent<Props & WithRouterPr
});
};
+ hasAutoImport(state: State, paid?: boolean): state is StateWithAutoImport {
+ return Boolean(state.almApplication && !paid);
+ }
+
setValidOrgKey = (almOrganization: AlmOrganization) => {
const key = slugify(almOrganization.key);
const keys = [key, ...times(9, i => `${key}-${i + 1}`)];
@@ -227,7 +236,7 @@ export class CreateOrganization extends React.PureComponent<Props & WithRouterPr
};
onTabChange = (tab: TabKeys) => {
- this.updateUrl({ tab });
+ this.updateUrlState({ tab });
};
stopLoading = () => {
@@ -236,7 +245,15 @@ export class CreateOrganization extends React.PureComponent<Props & WithRouterPr
}
};
- updateUrl = (state: Partial<LocationState> = {}) => {
+ updateUrlQuery = (query: Partial<Query> = {}) => {
+ this.props.router.push({
+ pathname: this.props.location.pathname,
+ query: serializeQuery({ ...parseQuery(this.props.location.query), ...query }),
+ state: this.props.location.state
+ });
+ };
+
+ updateUrlState = (state: Partial<LocationState> = {}) => {
this.props.router.replace({
pathname: this.props.location.pathname,
query: this.props.location.query,
@@ -246,36 +263,36 @@ export class CreateOrganization extends React.PureComponent<Props & WithRouterPr
renderContent = (almInstallId?: string, importPersonalOrg?: Organization) => {
const { currentUser, location } = this.props;
- const { almApplication, almOrganization } = this.state;
- const state: LocationState = location.state || {};
+ const { state } = this;
+ const { almOrganization } = state;
+ const { paid, tab = 'auto' } = (location.state || {}) as LocationState;
- if (importPersonalOrg && almOrganization && almApplication) {
+ if (importPersonalOrg && almOrganization && state.almApplication) {
return (
<AutoPersonalOrganizationBind
- almApplication={almApplication}
+ almApplication={state.almApplication}
almInstallId={almInstallId}
almOrganization={almOrganization}
importPersonalOrg={importPersonalOrg}
onOrgCreated={this.handleOrgCreated}
updateOrganization={this.props.updateOrganization}
+ updateUrlQuery={this.updateUrlQuery}
/>
);
}
- const showManualTab = state.tab === 'manual' && !almOrganization;
return (
<>
- {almApplication && (
+ {this.hasAutoImport(state, paid) && (
<Tabs<TabKeys>
onChange={this.onTabChange}
- selected={showManualTab ? 'manual' : 'auto'}
+ selected={tab || 'auto'}
tabs={[
{
key: 'auto',
- node: translate('onboarding.import_organization', almApplication.key)
+ node: translate('onboarding.import_organization', state.almApplication.key)
},
{
- disabled: Boolean(almOrganization),
key: 'manual',
node: translate('onboarding.create_organization.create_manually')
}
@@ -283,27 +300,30 @@ export class CreateOrganization extends React.PureComponent<Props & WithRouterPr
/>
)}
- {showManualTab || !almApplication ? (
- <ManualOrganizationCreate
- createOrganization={this.props.createOrganization}
- deleteOrganization={this.props.deleteOrganization}
- onOrgCreated={this.handleOrgCreated}
- onlyPaid={state.paid}
- subscriptionPlans={this.state.subscriptionPlans}
- />
- ) : (
+ <ManualOrganizationCreate
+ className={classNames({ hidden: tab !== 'manual' && this.hasAutoImport(state, paid) })}
+ createOrganization={this.props.createOrganization}
+ deleteOrganization={this.props.deleteOrganization}
+ onOrgCreated={this.handleOrgCreated}
+ onlyPaid={paid}
+ subscriptionPlans={this.state.subscriptionPlans}
+ />
+
+ {this.hasAutoImport(state, paid) && (
<AutoOrganizationCreate
- almApplication={almApplication}
+ almApplication={state.almApplication}
almInstallId={almInstallId}
almOrganization={almOrganization}
almUnboundApplications={this.state.almUnboundApplications}
boundOrganization={this.state.boundOrganization}
+ className={classNames({ hidden: tab !== 'auto' })}
createOrganization={this.props.createOrganization}
onOrgCreated={this.handleOrgCreated}
unboundOrganizations={this.props.userOrganizations.filter(
({ actions = {}, alm, key }) =>
!alm && key !== currentUser.personalOrganization && actions.admin
)}
+ updateUrlQuery={this.updateUrlQuery}
/>
)}
</>
@@ -325,9 +345,6 @@ export class CreateOrganization extends React.PureComponent<Props & WithRouterPr
const header = importPersonalOrg
? translate('onboarding.import_organization.personal.page.header')
: translate('onboarding.create_organization.page.header');
- const description = importPersonalOrg
- ? translate('onboarding.import_organization.personal.page.description')
- : translate('onboarding.create_organization.page.description');
const startedPrice = subscriptionPlans && subscriptionPlans[0] && subscriptionPlans[0].price;
const formattedPrice = formatPrice(startedPrice);
@@ -337,23 +354,24 @@ export class CreateOrganization extends React.PureComponent<Props & WithRouterPr
<div className="sonarcloud page page-limited">
<header className="page-header">
<h1 className="page-title big-spacer-bottom">{header}</h1>
- {startedPrice !== undefined && (
- <p className="page-description">
- <FormattedMessage
- defaultMessage={description}
- id={description}
- values={{
- break: <br />,
- price: formattedPrice,
- more: (
- <Link target="_blank" to="/documentation/sonarcloud-pricing/">
- {translate('learn_more')}
- </Link>
- )
- }}
- />
- </p>
- )}
+ {!importPersonalOrg &&
+ startedPrice !== undefined && (
+ <p className="page-description">
+ <FormattedMessage
+ defaultMessage={translate('onboarding.create_organization.page.description')}
+ id="onboarding.create_organization.page.description"
+ values={{
+ break: <br />,
+ price: formattedPrice,
+ more: (
+ <Link target="_blank" to="/documentation/sonarcloud-pricing/">
+ {translate('learn_more')}
+ </Link>
+ )
+ }}
+ />
+ </p>
+ )}
</header>
{this.state.loading ? (
<DeferredSpinner />
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 cda7e48af9e..4f67564ddbf 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
@@ -27,6 +27,7 @@ import { translate } from '../../../helpers/l10n';
interface Props {
createOrganization: (organization: OrganizationBase) => Promise<Organization>;
+ className?: string;
deleteOrganization: (key: string) => Promise<void>;
onOrgCreated: (organization: string) => void;
onlyPaid?: boolean;
@@ -101,12 +102,12 @@ export default class ManualOrganizationCreate extends React.PureComponent<Props,
};
render() {
- const { subscriptionPlans } = this.props;
+ const { className, subscriptionPlans } = this.props;
const startedPrice = subscriptionPlans && subscriptionPlans[0] && subscriptionPlans[0].price;
const formattedPrice = formatPrice(startedPrice);
return (
- <>
+ <div className={className}>
<OrganizationDetailsStep
finished={this.state.organization !== undefined}
onOpen={this.handleOrganizationDetailsStepOpen}
@@ -131,7 +132,7 @@ export default class ManualOrganizationCreate extends React.PureComponent<Props,
subscriptionPlans={subscriptionPlans}
/>
)}
- </>
+ </div>
);
}
}
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
index f6b4b5ac91b..de7330f912f 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/OrganizationDetailsForm.tsx
+++ b/server/sonar-web/src/main/js/apps/create/organization/OrganizationDetailsForm.tsx
@@ -18,12 +18,13 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
+import DeferredSpinner from '../../../components/common/DeferredSpinner';
+import DropdownIcon from '../../../components/icons-components/DropdownIcon';
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';
@@ -88,20 +89,20 @@ export default class OrganizationDetailsForm extends React.PureComponent<Props,
this.setState(state => ({ additional: !state.additional }));
};
- handleKeyUpdate = (key: string | undefined) => {
- this.setState({ key });
- };
-
- handleNameUpdate = (name: string | undefined) => {
- this.setState({ name });
+ handleAvatarUpdate = (avatar: string | undefined) => {
+ this.setState({ avatar });
};
handleDescriptionUpdate = (description: string | undefined) => {
this.setState({ description });
};
- handleAvatarUpdate = (avatar: string | undefined) => {
- this.setState({ avatar });
+ handleKeyUpdate = (key: string | undefined) => {
+ this.setState({ key });
+ };
+
+ handleNameUpdate = (name: string | undefined) => {
+ this.setState({ name });
};
handleUrlUpdate = (url: string | undefined) => {
@@ -132,13 +133,13 @@ export default class OrganizationDetailsForm extends React.PureComponent<Props,
};
render() {
+ const { submitting } = this.state;
+ const { keyReadOnly } = this.props;
return (
<form id="organization-form" onSubmit={this.handleSubmit}>
- <OrganizationKeyInput
- initialValue={this.state.key}
- onChange={this.handleKeyUpdate}
- readOnly={this.props.keyReadOnly}
- />
+ {!keyReadOnly && (
+ <OrganizationKeyInput initialValue={this.state.key} onChange={this.handleKeyUpdate} />
+ )}
<div className="big-spacer-top">
<ResetButtonLink onClick={this.handleAdditionalClick}>
{translate(
@@ -160,23 +161,25 @@ export default class OrganizationDetailsForm extends React.PureComponent<Props,
<OrganizationAvatarInput
initialValue={this.state.avatar}
name={this.state.name}
- onChange={this.handleDescriptionUpdate}
+ onChange={this.handleAvatarUpdate}
/>
</div>
<div className="big-spacer-top">
<OrganizationDescriptionInput
initialValue={this.state.description}
- onChange={this.handleAvatarUpdate}
+ onChange={this.handleDescriptionUpdate}
/>
</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)}>
+
+ <div className="display-flex-center big-spacer-top">
+ <SubmitButton disabled={submitting || !this.canSubmit(this.state)}>
{this.props.submitText}
</SubmitButton>
+ {submitting && <DeferredSpinner className="spacer-left" />}
</div>
</form>
);
diff --git a/server/sonar-web/src/main/js/apps/create/organization/PlanStep.tsx b/server/sonar-web/src/main/js/apps/create/organization/PlanStep.tsx
index f7b7a0f6a42..8a5517068c8 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/PlanStep.tsx
+++ b/server/sonar-web/src/main/js/apps/create/organization/PlanStep.tsx
@@ -91,6 +91,7 @@ export default class PlanStep extends React.PureComponent<Props, State> {
};
renderForm = () => {
+ const { submitting } = this.state;
return (
<div className="boxed-group-inner">
{this.state.ready && (
@@ -122,10 +123,10 @@ export default class PlanStep extends React.PureComponent<Props, State> {
</BillingForm>
) : (
<div className="display-flex-center big-spacer-top">
- <SubmitButton disabled={this.state.submitting} onClick={this.handleFreePlanSubmit}>
+ <SubmitButton disabled={submitting} onClick={this.handleFreePlanSubmit}>
{translate('my_account.create_organization')}
</SubmitButton>
- {this.state.submitting && <DeferredSpinner className="spacer-left" />}
+ {submitting && <DeferredSpinner className="spacer-left" />}
</div>
)}
</>
diff --git a/server/sonar-web/src/main/js/apps/create/organization/ChooseRemoteOrganizationStep.tsx b/server/sonar-web/src/main/js/apps/create/organization/RemoteOrganizationChoose.tsx
index 02447552553..149586bde0d 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/ChooseRemoteOrganizationStep.tsx
+++ b/server/sonar-web/src/main/js/apps/create/organization/RemoteOrganizationChoose.tsx
@@ -25,7 +25,6 @@ import { serializeQuery } from './utils';
import IdentityProviderLink from '../../../components/ui/IdentityProviderLink';
import OrganizationAvatar from '../../../components/common/OrganizationAvatar';
import Select from '../../../components/controls/Select';
-import Step from '../../tutorials/components/Step';
import { Alert } from '../../../components/ui/Alert';
import { SubmitButton } from '../../../components/ui/buttons';
import {
@@ -50,10 +49,7 @@ interface State {
unboundInstallationId: string;
}
-export class ChooseRemoteOrganizationStep extends React.PureComponent<
- Props & WithRouterProps,
- State
-> {
+export class RemoteOrganizationChoose extends React.PureComponent<Props & WithRouterProps, State> {
state: State = { unboundInstallationId: '' };
handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
@@ -90,7 +86,7 @@ export class ChooseRemoteOrganizationStep extends React.PureComponent<
);
};
- renderForm = () => {
+ render() {
const {
almApplication,
almInstallId,
@@ -144,7 +140,7 @@ export class ChooseRemoteOrganizationStep extends React.PureComponent<
</Alert>
)}
<div className="display-flex-center">
- <div className="display-inline-block abs-width-400">
+ <div className="display-inline-block">
<IdentityProviderLink
className="display-inline-block"
identityProvider={almApplication}
@@ -194,25 +190,7 @@ export class ChooseRemoteOrganizationStep extends React.PureComponent<
</div>
</div>
);
- };
-
- renderResult = () => {
- return null;
- };
-
- render() {
- return (
- <Step
- finished={false}
- onOpen={() => {}}
- open={true}
- renderForm={this.renderForm}
- renderResult={this.renderResult}
- stepNumber={1}
- stepTitle={translate('onboarding.import_organization.import_org_details')}
- />
- );
}
}
-export default withRouter(ChooseRemoteOrganizationStep);
+export default withRouter(RemoteOrganizationChoose);
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 6cd346021e2..aaed1cd3a24 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
@@ -20,7 +20,7 @@
import * as React from 'react';
import { shallow } from 'enzyme';
import AutoOrganizationCreate from '../AutoOrganizationCreate';
-import { waitAndUpdate } from '../../../../helpers/testUtils';
+import { waitAndUpdate, click } from '../../../../helpers/testUtils';
import { bindAlmOrganization } from '../../../../api/alm-integration';
jest.mock('../../../../api/alm-integration', () => ({
@@ -58,6 +58,18 @@ it('should render prefilled and create org', async () => {
expect(onOrgCreated).toBeCalledWith('foo');
});
+it('should allow to cancel org import', () => {
+ const updateUrlQuery = jest.fn().mockResolvedValue({ key: 'foo' });
+ const wrapper = shallowRender({
+ almInstallId: 'id-foo',
+ almOrganization: { ...organization, personal: false },
+ updateUrlQuery
+ });
+
+ click(wrapper.find('DeleteButton'));
+ expect(updateUrlQuery).toBeCalledWith({ almInstallId: undefined, almKey: undefined });
+});
+
it('should display choice between import or creation', () => {
const wrapper = shallowRender({
almInstallId: 'id-foo',
@@ -109,6 +121,7 @@ function shallowRender(props: Partial<AutoOrganizationCreate['props']> = {}) {
createOrganization={jest.fn()}
onOrgCreated={jest.fn()}
unboundOrganizations={[]}
+ updateUrlQuery={jest.fn()}
{...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
index dd1eebc4620..eeb1de2935d 100644
--- 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
@@ -20,10 +20,11 @@
import * as React from 'react';
import { shallow } from 'enzyme';
import AutoPersonalOrganizationBind from '../AutoPersonalOrganizationBind';
-import { waitAndUpdate } from '../../../../helpers/testUtils';
+import { waitAndUpdate, click } from '../../../../helpers/testUtils';
+
+const personalOrg = { key: 'personalorg', name: 'Personal Org' };
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({
@@ -42,6 +43,18 @@ it('should render correctly', async () => {
expect(onOrgCreated).toBeCalledWith(personalOrg.key);
});
+it('should allow to cancel org import', () => {
+ const updateUrlQuery = jest.fn();
+ const wrapper = shallowRender({
+ almInstallId: 'id-foo',
+ importPersonalOrg: personalOrg,
+ updateUrlQuery
+ });
+
+ click(wrapper.find('DeleteButton'));
+ expect(updateUrlQuery).toBeCalledWith({ almInstallId: undefined, almKey: undefined });
+});
+
function shallowRender(props: Partial<AutoPersonalOrganizationBind['props']> = {}) {
return shallow(
<AutoPersonalOrganizationBind
@@ -63,6 +76,7 @@ function shallowRender(props: Partial<AutoPersonalOrganizationBind['props']> = {
importPersonalOrg={{ key: 'personalorg', name: 'Personal Org' }}
onOrgCreated={jest.fn()}
updateOrganization={jest.fn()}
+ updateUrlQuery={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 14434ece244..febe22899e8 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
@@ -185,9 +185,11 @@ it('should switch tabs', async () => {
expect(wrapper).toMatchSnapshot();
(wrapper.find('Tabs').prop('onChange') as Function)('manual');
- expect(wrapper.find('ManualOrganizationCreate').exists()).toBeTruthy();
+ expect(wrapper.find('ManualOrganizationCreate').hasClass('hidden')).toBeFalsy();
+ expect(wrapper.find('AutoOrganizationCreate').hasClass('hidden')).toBeTruthy();
(wrapper.find('Tabs').prop('onChange') as Function)('auto');
- expect(wrapper.find('AutoOrganizationCreate').exists()).toBeTruthy();
+ expect(wrapper.find('AutoOrganizationCreate').hasClass('hidden')).toBeFalsy();
+ expect(wrapper.find('ManualOrganizationCreate').hasClass('hidden')).toBeTruthy();
});
it('should reload the alm organization when the url query changes', async () => {
diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/ChooseRemoteOrganizationStep-test.tsx b/server/sonar-web/src/main/js/apps/create/organization/__tests__/RemoteOrganizationChoose-test.tsx
index 5c08a1d8072..86b68fd9370 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/ChooseRemoteOrganizationStep-test.tsx
+++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/RemoteOrganizationChoose-test.tsx
@@ -19,7 +19,7 @@
*/
import * as React from 'react';
import { shallow } from 'enzyme';
-import { ChooseRemoteOrganizationStep } from '../ChooseRemoteOrganizationStep';
+import { RemoteOrganizationChoose } from '../RemoteOrganizationChoose';
import { mockRouter, submit } from '../../../../helpers/testUtils';
it('should render', () => {
@@ -57,10 +57,10 @@ it('should display already bound alert message', () => {
).toMatchSnapshot();
});
-function shallowRender(props: Partial<ChooseRemoteOrganizationStep['props']> = {}) {
+function shallowRender(props: Partial<RemoteOrganizationChoose['props']> = {}) {
return shallow(
// @ts-ignore avoid passing everything from WithRouterProps
- <ChooseRemoteOrganizationStep
+ <RemoteOrganizationChoose
almApplication={{
backgroundColor: 'blue',
iconPath: 'icon/path',
@@ -72,5 +72,5 @@ function shallowRender(props: Partial<ChooseRemoteOrganizationStep['props']> = {
router={mockRouter()}
{...props}
/>
- ).dive();
+ );
}
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
index 612312edf9d..16cca64c656 100644
--- 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
@@ -21,7 +21,7 @@ exports[`should render correctly 1`] = `
}
/>
<div
- className="big-spacer-top"
+ className="display-flex-center big-spacer-top"
>
<SubmitButton
disabled={false}
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 b0aa2e02a1a..985f767d6c7 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,136 +1,153 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`should display choice between import or creation 1`] = `
-<OrganizationDetailsStep
- finished={false}
- onOpen={[Function]}
- open={true}
- organization={
- Object {
- "avatar": "http://example.com/avatar",
- "description": "description-foo",
- "key": "key-foo",
- "name": "name-foo",
- "personal": false,
- "url": "http://example.com/foo",
- }
- }
+<div
+ className="boxed-group"
>
<div
- className="huge-spacer-bottom"
+ className="boxed-group-header"
+ >
+ <h2>
+ onboarding.import_organization.import_org_details
+ </h2>
+ </div>
+ <div
+ className="boxed-group-inner"
>
- <p
- className="big-spacer-bottom"
+ <div
+ className="huge-spacer-bottom"
>
- <FormattedMessage
- defaultMessage="onboarding.import_organization_x"
- id="onboarding.import_organization_x"
- values={
- Object {
- "avatar": <img
- alt="BitBucket"
- className="little-spacer-left"
- src="/images/sonarcloud/bitbucket.svg"
- width={16}
- />,
- "name": <strong>
- name-foo
- </strong>,
+ <p
+ className="display-flex-center big-spacer-bottom"
+ >
+ <FormattedMessage
+ defaultMessage="onboarding.import_organization_x"
+ id="onboarding.import_organization_x"
+ values={
+ Object {
+ "avatar": <img
+ alt="BitBucket"
+ className="little-spacer-left"
+ src="/images/sonarcloud/bitbucket.svg"
+ width={16}
+ />,
+ "name": <strong>
+ name-foo
+ </strong>,
+ }
}
+ />
+ <DeleteButton
+ className="little-spacer-left"
+ onClick={[Function]}
+ />
+ </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={null}
/>
- </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={null}
- />
+ </div>
</div>
-</OrganizationDetailsStep>
+</div>
`;
exports[`should render prefilled and create org 1`] = `
-<OrganizationDetailsStep
- finished={false}
- onOpen={[Function]}
- open={true}
- organization={
- Object {
- "avatar": "http://example.com/avatar",
- "description": "description-foo",
- "key": "key-foo",
- "name": "name-foo",
- "personal": false,
- "url": "http://example.com/foo",
- }
- }
+<div
+ className="boxed-group"
>
<div
- className="huge-spacer-bottom"
+ className="boxed-group-header"
+ >
+ <h2>
+ onboarding.import_organization.import_org_details
+ </h2>
+ </div>
+ <div
+ className="boxed-group-inner"
>
- <p
- className="big-spacer-bottom"
+ <div
+ className="huge-spacer-bottom"
>
- <FormattedMessage
- defaultMessage="onboarding.import_organization_x"
- id="onboarding.import_organization_x"
- values={
- Object {
- "avatar": <img
- alt="BitBucket"
- className="little-spacer-left"
- src="/images/sonarcloud/bitbucket.svg"
- width={16}
- />,
- "name": <strong>
- name-foo
- </strong>,
+ <p
+ className="display-flex-center big-spacer-bottom"
+ >
+ <FormattedMessage
+ defaultMessage="onboarding.import_organization_x"
+ id="onboarding.import_organization_x"
+ values={
+ Object {
+ "avatar": <img
+ alt="BitBucket"
+ className="little-spacer-left"
+ src="/images/sonarcloud/bitbucket.svg"
+ width={16}
+ />,
+ "name": <strong>
+ name-foo
+ </strong>,
+ }
}
+ />
+ <DeleteButton
+ className="little-spacer-left"
+ onClick={[Function]}
+ />
+ </p>
+ </div>
+ <OrganizationDetailsForm
+ onContinue={[Function]}
+ organization={
+ Object {
+ "avatar": "http://example.com/avatar",
+ "description": "description-foo",
+ "key": "key-foo",
+ "name": "name-foo",
+ "personal": false,
+ "url": "http://example.com/foo",
}
- />
- </p>
- </div>
- <OrganizationDetailsForm
- onContinue={[Function]}
- organization={
- Object {
- "avatar": "http://example.com/avatar",
- "description": "description-foo",
- "key": "key-foo",
- "name": "name-foo",
- "personal": false,
- "url": "http://example.com/foo",
}
- }
- submitText="onboarding.import_organization.import"
- />
-</OrganizationDetailsStep>
+ submitText="onboarding.import_organization.import"
+ />
+ </div>
+</div>
`;
exports[`should render with import org button 1`] = `
-<withRouter(ChooseRemoteOrganizationStep)
- almApplication={
- Object {
- "backgroundColor": "#0052CC",
- "iconPath": "\\"/static/authbitbucket/bitbucket.svg\\"",
- "installationUrl": "https://bitbucket.org/install/app",
- "key": "bitbucket",
- "name": "BitBucket",
+<div
+ className="boxed-group"
+>
+ <div
+ className="boxed-group-header"
+ >
+ <h2>
+ onboarding.import_organization.import_org_details
+ </h2>
+ </div>
+ <withRouter(RemoteOrganizationChoose)
+ almApplication={
+ Object {
+ "backgroundColor": "#0052CC",
+ "iconPath": "\\"/static/authbitbucket/bitbucket.svg\\"",
+ "installationUrl": "https://bitbucket.org/install/app",
+ "key": "bitbucket",
+ "name": "BitBucket",
+ }
}
- }
- almUnboundApplications={Array []}
-/>
+ almUnboundApplications={Array []}
+ />
+</div>
`;
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
index 2b2026ad813..b1487a7738b 100644
--- 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
@@ -1,60 +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",
- }
- }
+<div
+ className="boxed-group"
>
<div
- className="huge-spacer-bottom"
+ className="boxed-group-inner"
>
- <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",
+ <div
+ className="display-flex-center big-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>,
+ small={true}
+ />,
+ "personalName": <strong>
+ Personal Org
+ </strong>,
+ }
+ }
+ />
+ <DeleteButton
+ className="little-spacer-left"
+ onClick={[Function]}
+ />
+ </div>
+ <OrganizationDetailsForm
+ keyReadOnly={true}
+ onContinue={[Function]}
+ organization={
+ Object {
+ "key": "personalorg",
+ "name": "Personal Org",
}
}
+ submitText="onboarding.import_organization.bind"
/>
</div>
- <OrganizationDetailsForm
- keyReadOnly={true}
- onContinue={[Function]}
- organization={
- Object {
- "key": "personalorg",
- "name": "Personal Org",
- }
- }
- submitText="onboarding.import_organization.bind"
- />
-</OrganizationDetailsStep>
+</div>
`;
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
deleted file mode 100644
index 6faa756f708..00000000000
--- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/ChooseRemoteOrganizationStep-test.tsx.snap
+++ /dev/null
@@ -1,222 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should display already bound alert message 1`] = `
-<Alert
- className="big-spacer-bottom width-60"
- variant="error"
->
- <FormattedMessage
- defaultMessage="onboarding.import_organization.already_bound_x"
- id="onboarding.import_organization.already_bound_x"
- values={
- Object {
- "avatar": <img
- alt="GitHub"
- className="little-spacer-left"
- src="/images/sonarcloud/github.svg"
- width={16}
- />,
- "boundAvatar": <OrganizationAvatar
- className="little-spacer-left"
- organization={
- Object {
- "avatar": "bound-avatar",
- "key": "bound",
- "name": "Bound",
- }
- }
- small={true}
- />,
- "boundName": <strong>
- Bound
- </strong>,
- "name": <strong>
- Foo
- </strong>,
- }
- }
- />
-</Alert>
-`;
-
-exports[`should display an alert message 1`] = `
-<Alert
- className="big-spacer-bottom width-60"
- variant="error"
->
- <div
- className="markdown"
- >
- onboarding.import_organization.org_not_found
- <ul>
- <li>
- onboarding.import_organization.org_not_found.tips_1
- </li>
- <li>
- onboarding.import_organization.org_not_found.tips_2
- </li>
- </ul>
- </div>
-</Alert>
-`;
-
-exports[`should display unbound installations 1`] = `
-<div
- className="boxed-group onboarding-step is-open"
->
- <div
- className="onboarding-step-number"
- >
- 1
- </div>
- <div
- className="boxed-group-header"
- >
- <h2>
- onboarding.import_organization.import_org_details
- </h2>
- </div>
- <div
- className=""
- >
- <div
- className="boxed-group-inner"
- >
- <div
- className="display-flex-center"
- >
- <div
- className="display-inline-block abs-width-400"
- >
- <IdentityProviderLink
- className="display-inline-block"
- identityProvider={
- Object {
- "backgroundColor": "blue",
- "iconPath": "icon/path",
- "installationUrl": "https://alm.application.url",
- "key": "github",
- "name": "GitHub",
- }
- }
- small={true}
- url="https://alm.application.url"
- >
- onboarding.import_organization.choose_organization_button.github
- </IdentityProviderLink>
- </div>
- <div
- className="display-flex-stretch"
- >
- <div
- className="vertical-pipe-separator"
- >
- <div
- className="vertical-separator "
- />
- <span
- className="note"
- >
- or
- </span>
- <div
- className="vertical-separator"
- />
- </div>
- <form
- className="big-spacer-top big-spacer-bottom"
- onSubmit={[Function]}
- >
- <div
- className="form-field abs-width-400"
- >
- <label
- htmlFor="select-unbound-installation"
- >
- onboarding.import_organization.choose_unbound_installation.github
- </label>
- <Select
- className="input-super-large"
- clearable={false}
- id="select-unbound-installation"
- labelKey="name"
- onChange={[Function]}
- optionRenderer={[Function]}
- options={
- Array [
- Object {
- "installationId": "12345",
- "key": "foo",
- "name": "Foo",
- },
- ]
- }
- placeholder="onboarding.import_organization.choose_organization"
- value=""
- valueKey="installationId"
- valueRenderer={[Function]}
- />
- </div>
- <SubmitButton
- disabled={true}
- >
- continue
- </SubmitButton>
- </form>
- </div>
- </div>
- </div>
- </div>
-</div>
-`;
-
-exports[`should render 1`] = `
-<div
- className="boxed-group onboarding-step is-open"
->
- <div
- className="onboarding-step-number"
- >
- 1
- </div>
- <div
- className="boxed-group-header"
- >
- <h2>
- onboarding.import_organization.import_org_details
- </h2>
- </div>
- <div
- className=""
- >
- <div
- className="boxed-group-inner"
- >
- <div
- className="display-flex-center"
- >
- <div
- className="display-inline-block abs-width-400"
- >
- <IdentityProviderLink
- className="display-inline-block"
- identityProvider={
- Object {
- "backgroundColor": "blue",
- "iconPath": "icon/path",
- "installationUrl": "https://alm.application.url",
- "key": "github",
- "name": "GitHub",
- }
- }
- small={true}
- url="https://alm.application.url"
- >
- onboarding.import_organization.choose_organization_button.github
- </IdentityProviderLink>
- </div>
- </div>
- </div>
- </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 748f5e88a96..86a76be9310 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
@@ -25,28 +25,6 @@ exports[`should render with auto personal organization bind page 2`] = `
>
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={
@@ -78,6 +56,7 @@ exports[`should render with auto personal organization bind page 2`] = `
}
onOrgCreated={[Function]}
updateOrganization={[MockFunction]}
+ updateUrlQuery={[Function]}
/>
</div>
</Fragment>
@@ -135,13 +114,30 @@ exports[`should render with auto tab displayed 1`] = `
"node": "onboarding.import_organization.github",
},
Object {
- "disabled": false,
"key": "manual",
"node": "onboarding.create_organization.create_manually",
},
]
}
/>
+ <ManualOrganizationCreate
+ className="hidden"
+ createOrganization={[MockFunction]}
+ deleteOrganization={[MockFunction]}
+ onOrgCreated={[Function]}
+ subscriptionPlans={
+ Array [
+ Object {
+ "maxNcloc": 100000,
+ "price": 10,
+ },
+ Object {
+ "maxNcloc": 250000,
+ "price": 75,
+ },
+ ]
+ }
+ />
<AutoOrganizationCreate
almApplication={
Object {
@@ -153,6 +149,7 @@ exports[`should render with auto tab displayed 1`] = `
}
}
almUnboundApplications={Array []}
+ className=""
createOrganization={[MockFunction]}
onOrgCreated={[Function]}
unboundOrganizations={
@@ -166,6 +163,7 @@ exports[`should render with auto tab displayed 1`] = `
},
]
}
+ updateUrlQuery={[Function]}
/>
</div>
</Fragment>
@@ -229,13 +227,30 @@ exports[`should render with auto tab selected and manual disabled 2`] = `
"node": "onboarding.import_organization.github",
},
Object {
- "disabled": true,
"key": "manual",
"node": "onboarding.create_organization.create_manually",
},
]
}
/>
+ <ManualOrganizationCreate
+ className="hidden"
+ createOrganization={[MockFunction]}
+ deleteOrganization={[MockFunction]}
+ onOrgCreated={[Function]}
+ subscriptionPlans={
+ Array [
+ Object {
+ "maxNcloc": 100000,
+ "price": 10,
+ },
+ Object {
+ "maxNcloc": 250000,
+ "price": 75,
+ },
+ ]
+ }
+ />
<AutoOrganizationCreate
almApplication={
Object {
@@ -258,6 +273,7 @@ exports[`should render with auto tab selected and manual disabled 2`] = `
}
}
almUnboundApplications={Array []}
+ className=""
createOrganization={[MockFunction]}
onOrgCreated={[Function]}
unboundOrganizations={
@@ -271,6 +287,7 @@ exports[`should render with auto tab selected and manual disabled 2`] = `
},
]
}
+ updateUrlQuery={[Function]}
/>
</div>
</Fragment>
@@ -319,6 +336,7 @@ exports[`should render with manual tab displayed 1`] = `
</p>
</header>
<ManualOrganizationCreate
+ className=""
createOrganization={[MockFunction]}
deleteOrganization={[MockFunction]}
onOrgCreated={[Function]}
@@ -391,13 +409,30 @@ exports[`should switch tabs 1`] = `
"node": "onboarding.import_organization.github",
},
Object {
- "disabled": false,
"key": "manual",
"node": "onboarding.create_organization.create_manually",
},
]
}
/>
+ <ManualOrganizationCreate
+ className="hidden"
+ createOrganization={[MockFunction]}
+ deleteOrganization={[MockFunction]}
+ onOrgCreated={[Function]}
+ subscriptionPlans={
+ Array [
+ Object {
+ "maxNcloc": 100000,
+ "price": 10,
+ },
+ Object {
+ "maxNcloc": 250000,
+ "price": 75,
+ },
+ ]
+ }
+ />
<AutoOrganizationCreate
almApplication={
Object {
@@ -409,6 +444,7 @@ exports[`should switch tabs 1`] = `
}
}
almUnboundApplications={Array []}
+ className=""
createOrganization={[MockFunction]}
onOrgCreated={[Function]}
unboundOrganizations={
@@ -422,6 +458,7 @@ exports[`should switch tabs 1`] = `
},
]
}
+ updateUrlQuery={[Function]}
/>
</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 6d36c3862b3..d538064e72c 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
@@ -1,7 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`should render and create organization 1`] = `
-<Fragment>
+<div>
<OrganizationDetailsStep
finished={false}
onOpen={[Function]}
@@ -32,11 +32,11 @@ exports[`should render and create organization 1`] = `
]
}
/>
-</Fragment>
+</div>
`;
exports[`should render and create organization 2`] = `
-<Fragment>
+<div>
<OrganizationDetailsStep
finished={true}
onOpen={[Function]}
@@ -85,5 +85,5 @@ exports[`should render and create organization 2`] = `
]
}
/>
-</Fragment>
+</div>
`;
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
index 3acf5cb5e3a..8843a17679c 100644
--- 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
@@ -60,7 +60,7 @@ exports[`should render form 1`] = `
</div>
</div>
<div
- className="big-spacer-top"
+ className="display-flex-center big-spacer-top"
>
<SubmitButton
disabled={true}
diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/RemoteOrganizationChoose-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/RemoteOrganizationChoose-test.tsx.snap
new file mode 100644
index 00000000000..dfc8f97b04b
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/RemoteOrganizationChoose-test.tsx.snap
@@ -0,0 +1,182 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should display already bound alert message 1`] = `
+<Alert
+ className="big-spacer-bottom width-60"
+ variant="error"
+>
+ <FormattedMessage
+ defaultMessage="onboarding.import_organization.already_bound_x"
+ id="onboarding.import_organization.already_bound_x"
+ values={
+ Object {
+ "avatar": <img
+ alt="GitHub"
+ className="little-spacer-left"
+ src="/images/sonarcloud/github.svg"
+ width={16}
+ />,
+ "boundAvatar": <OrganizationAvatar
+ className="little-spacer-left"
+ organization={
+ Object {
+ "avatar": "bound-avatar",
+ "key": "bound",
+ "name": "Bound",
+ }
+ }
+ small={true}
+ />,
+ "boundName": <strong>
+ Bound
+ </strong>,
+ "name": <strong>
+ Foo
+ </strong>,
+ }
+ }
+ />
+</Alert>
+`;
+
+exports[`should display an alert message 1`] = `
+<Alert
+ className="big-spacer-bottom width-60"
+ variant="error"
+>
+ <div
+ className="markdown"
+ >
+ onboarding.import_organization.org_not_found
+ <ul>
+ <li>
+ onboarding.import_organization.org_not_found.tips_1
+ </li>
+ <li>
+ onboarding.import_organization.org_not_found.tips_2
+ </li>
+ </ul>
+ </div>
+</Alert>
+`;
+
+exports[`should display unbound installations 1`] = `
+<div
+ className="boxed-group-inner"
+>
+ <div
+ className="display-flex-center"
+ >
+ <div
+ className="display-inline-block"
+ >
+ <IdentityProviderLink
+ className="display-inline-block"
+ identityProvider={
+ Object {
+ "backgroundColor": "blue",
+ "iconPath": "icon/path",
+ "installationUrl": "https://alm.application.url",
+ "key": "github",
+ "name": "GitHub",
+ }
+ }
+ small={true}
+ url="https://alm.application.url"
+ >
+ onboarding.import_organization.choose_organization_button.github
+ </IdentityProviderLink>
+ </div>
+ <div
+ className="display-flex-stretch"
+ >
+ <div
+ className="vertical-pipe-separator"
+ >
+ <div
+ className="vertical-separator "
+ />
+ <span
+ className="note"
+ >
+ or
+ </span>
+ <div
+ className="vertical-separator"
+ />
+ </div>
+ <form
+ className="big-spacer-top big-spacer-bottom"
+ onSubmit={[Function]}
+ >
+ <div
+ className="form-field abs-width-400"
+ >
+ <label
+ htmlFor="select-unbound-installation"
+ >
+ onboarding.import_organization.choose_unbound_installation.github
+ </label>
+ <Select
+ className="input-super-large"
+ clearable={false}
+ id="select-unbound-installation"
+ labelKey="name"
+ onChange={[Function]}
+ optionRenderer={[Function]}
+ options={
+ Array [
+ Object {
+ "installationId": "12345",
+ "key": "foo",
+ "name": "Foo",
+ },
+ ]
+ }
+ placeholder="onboarding.import_organization.choose_organization"
+ value=""
+ valueKey="installationId"
+ valueRenderer={[Function]}
+ />
+ </div>
+ <SubmitButton
+ disabled={true}
+ >
+ continue
+ </SubmitButton>
+ </form>
+ </div>
+ </div>
+</div>
+`;
+
+exports[`should render 1`] = `
+<div
+ className="boxed-group-inner"
+>
+ <div
+ className="display-flex-center"
+ >
+ <div
+ className="display-inline-block"
+ >
+ <IdentityProviderLink
+ className="display-inline-block"
+ identityProvider={
+ Object {
+ "backgroundColor": "blue",
+ "iconPath": "icon/path",
+ "installationUrl": "https://alm.application.url",
+ "key": "github",
+ "name": "GitHub",
+ }
+ }
+ small={true}
+ url="https://alm.application.url"
+ >
+ onboarding.import_organization.choose_organization_button.github
+ </IdentityProviderLink>
+ </div>
+ </div>
+</div>
+`;