Browse Source

SONARCLOUD-138 Make forms clearer when upgrading an organization (#761)

tags/7.5
Stas Vilchik 5 years ago
parent
commit
f52d0c2586
21 changed files with 53 additions and 839 deletions
  1. 0
    1
      server/sonar-docs/src/tooltips/billing/coupon.md
  2. 2
    0
      server/sonar-web/src/main/js/app/components/extensions/exposeLibraries.ts
  3. 4
    20
      server/sonar-web/src/main/js/apps/create/organization/BillingFormShim.tsx
  4. 0
    91
      server/sonar-web/src/main/js/apps/create/organization/CardForm.tsx
  5. 0
    122
      server/sonar-web/src/main/js/apps/create/organization/CouponForm.tsx
  6. 0
    57
      server/sonar-web/src/main/js/apps/create/organization/PaymentMethodSelect.tsx
  7. 19
    28
      server/sonar-web/src/main/js/apps/create/organization/PlanStep.tsx
  8. 2
    15
      server/sonar-web/src/main/js/apps/create/organization/__mocks__/BillingFormShim.tsx
  9. 0
    1
      server/sonar-web/src/main/js/apps/create/organization/__tests__/BillingFormShim-test.tsx
  10. 0
    38
      server/sonar-web/src/main/js/apps/create/organization/__tests__/CardForm-test.tsx
  11. 0
    36
      server/sonar-web/src/main/js/apps/create/organization/__tests__/CouponForm-test.tsx
  12. 0
    34
      server/sonar-web/src/main/js/apps/create/organization/__tests__/PaymentMethodSelect-test.tsx
  13. 3
    44
      server/sonar-web/src/main/js/apps/create/organization/__tests__/PlanStep-test.tsx
  14. 0
    1
      server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/BillingFormShim-test.tsx.snap
  15. 0
    107
      server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/CardForm-test.tsx.snap
  16. 0
    22
      server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/CouponForm-test.tsx.snap
  17. 0
    79
      server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/PaymentMethodSelect-test.tsx.snap
  18. 12
    140
      server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/PlanStep-test.tsx.snap
  19. 4
    0
      server/sonar-web/src/main/js/apps/tutorials/styles.css
  20. 7
    2
      server/sonar-web/src/main/js/components/controls/HelpTooltip.tsx
  21. 0
    1
      sonar-core/src/main/resources/org/sonar/l10n/core.properties

+ 0
- 1
server/sonar-docs/src/tooltips/billing/coupon.md View File

@@ -1 +0,0 @@
A coupon is a way to pay for yearly subscriptions or to use other payment methods than card. Contact us for more information.

+ 2
- 0
server/sonar-web/src/main/js/app/components/extensions/exposeLibraries.ts View File

@@ -62,6 +62,7 @@ import ActionsDropdown, { ActionsDropdownItem } from '../../../components/contro
import ConfirmButton from '../../../components/controls/ConfirmButton';
import SimpleModal from '../../../components/controls/SimpleModal';
import SearchSelect from '../../../components/controls/SearchSelect';
import RadioToggle from '../../../components/controls/RadioToggle';

const exposeLibraries = () => {
const global = window as any;
@@ -103,6 +104,7 @@ const exposeLibraries = () => {
Modal,
PullRequestIcon,
QualifierIcon,
RadioToggle,
Rating,
ReloadButton,
ResetButtonLink,

+ 4
- 20
server/sonar-web/src/main/js/apps/create/organization/BillingFormShim.tsx View File

@@ -18,37 +18,21 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import { CurrentUser, SubscriptionPlan, Coupon } from '../../../app/types';
import { CurrentUser, SubscriptionPlan } from '../../../app/types';

interface ChildrenProps {
alertError: string | undefined;
couponValue: string;
onSubmit: React.FormEventHandler;
renderAdditionalInfo: () => React.ReactNode;
renderBillingNameInput: () => React.ReactNode;
renderBraintreeClient: () => React.ReactNode;
renderCountrySelect: () => React.ReactNode;
renderCouponInput: (children?: React.ReactNode) => React.ReactNode;
renderEmailInput: () => React.ReactNode;
renderNextCharge: () => React.ReactNode;
renderPlanSelect: () => React.ReactNode;
renderResetButton: () => React.ReactNode;
renderSpinner: () => React.ReactNode;
renderSubmitButton: (text?: string) => React.ReactNode;
renderTermsOfService: () => React.ReactNode;
renderTypeOfUseSelect: () => React.ReactNode;
renderFormFields: () => React.ReactElement<any>;
renderSubmitGroup: (submitText?: string) => React.ReactElement<any>;
}

interface Props {
children: (props: ChildrenProps) => React.ReactElement<any>;
country?: string;
initialCountry?: string;
currentUser: CurrentUser;
onClose: () => void;
onCommit: () => void;
onCouponUpdate?: (coupon?: Coupon) => void;
onFailToUpgrade?: () => void;
organizationKey: string | (() => Promise<string>);
skipBraintreeInit?: boolean;
subscriptionPlans: SubscriptionPlan[];
}


+ 0
- 91
server/sonar-web/src/main/js/apps/create/organization/CardForm.tsx View File

@@ -1,91 +0,0 @@
/*
* 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 * as classNames from 'classnames';
import BillingFormShim from './BillingFormShim';
import { withCurrentUser } from './withCurrentUser';
import { CurrentUser, SubscriptionPlan } from '../../../app/types';
import { translate } from '../../../helpers/l10n';

interface Props {
createOrganization: () => Promise<string>;
currentUser: CurrentUser;
onFailToUpgrade: () => void;
onSubmit: () => void;
subscriptionPlans: SubscriptionPlan[];
}

export class CardForm extends React.PureComponent<Props> {
handleClose = () => {
// do nothing
};

render() {
return (
<div className="huge-spacer-top">
<BillingFormShim
currentUser={this.props.currentUser}
onClose={this.handleClose}
onCommit={this.props.onSubmit}
onFailToUpgrade={this.props.onFailToUpgrade}
organizationKey={this.props.createOrganization}
subscriptionPlans={this.props.subscriptionPlans}>
{form => (
<form onSubmit={form.onSubmit}>
<div className="columns column-show-overflow">
<div className="column-half">
<h3>{translate('billing.upgrade.billing_info')}</h3>
{form.renderEmailInput()}
{form.renderTypeOfUseSelect()}
{form.renderBillingNameInput()}
{form.renderCountrySelect()}
{form.renderAdditionalInfo()}
</div>
<div className="column-half">
<h3>{translate('billing.upgrade.plan')}</h3>
{form.renderPlanSelect()}
<h3>{translate('billing.upgrade.card_info')}</h3>
{form.renderBraintreeClient()}
</div>
</div>
<div className="upgrade-footer big-spacer-top">
{form.renderNextCharge()}
<hr className="big-spacer-bottom" />
{form.alertError && <p className="alert alert-danger">{form.alertError}</p>}
</div>
<div
className={classNames({
'big-spacer-top': form.alertError !== undefined
})}>
{form.renderSpinner()}
{form.renderSubmitButton(
translate('onboarding.create_organization.create_and_upgrade')
)}
</div>
{form.renderTermsOfService()}
</form>
)}
</BillingFormShim>
</div>
);
}
}

export default withCurrentUser(CardForm);

+ 0
- 122
server/sonar-web/src/main/js/apps/create/organization/CouponForm.tsx View File

@@ -1,122 +0,0 @@
/*
* 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 * as classNames from 'classnames';
import BillingFormShim from './BillingFormShim';
import { withCurrentUser } from './withCurrentUser';
import { CurrentUser, Coupon } from '../../../app/types';
import { translate } from '../../../helpers/l10n';
import DocTooltip from '../../../components/docs/DocTooltip';

interface Props {
createOrganization: () => Promise<string>;
currentUser: CurrentUser;
onFailToUpgrade: () => void;
onSubmit: () => void;
}

interface State {
coupon?: Coupon;
}

export class CouponForm extends React.PureComponent<Props, State> {
state: State = {};

handleClose = () => {
// do nothing
};

handleCouponUpdate = (coupon?: Coupon) => {
this.setState({ coupon });
};

renderBillingInformation() {
if (!this.state.coupon || !this.state.coupon.billing) {
return null;
}
const { billing } = this.state.coupon;
return (
<div className="big-spacer-top big-spacer-bottom" id="coupon-billing-information">
<h3 className="note">{translate('billing.upgrade.billing_info')}</h3>
<ul className="note">
{Boolean(billing.name) && <li>{billing.name}</li>}
{Boolean(billing.address) && <li>{billing.address}</li>}
{Boolean(billing.country) && <li>{billing.country}</li>}
{Boolean(billing.email) && <li>{billing.email}</li>}
</ul>
</div>
);
}

render() {
return (
<div className="huge-spacer-top">
<BillingFormShim
currentUser={this.props.currentUser}
onClose={this.handleClose}
onCommit={this.props.onSubmit}
onCouponUpdate={this.handleCouponUpdate}
onFailToUpgrade={this.props.onFailToUpgrade}
organizationKey={this.props.createOrganization}
skipBraintreeInit={true}
subscriptionPlans={[]}>
{form => (
<form onSubmit={form.onSubmit}>
{form.renderCouponInput(
<label htmlFor="coupon">
{translate('billing.upgrade.coupon')}
<DocTooltip
className="little-spacer-left"
doc={import(/* webpackMode: "eager" */ 'Docs/tooltips/billing/coupon.md')}
/>
</label>
)}
{this.renderBillingInformation()}
{this.state.coupon &&
!this.state.coupon.billing && (
<>
<h3 className="big-spacer-top">{translate('billing.upgrade.billing_info')}</h3>
{form.renderEmailInput()}
{form.renderTypeOfUseSelect()}
{form.renderBillingNameInput()}
{form.renderCountrySelect()}
{form.renderAdditionalInfo()}
</>
)}
{this.state.coupon && (
<div className="big-spacer-bottom">{form.renderNextCharge()}</div>
)}
{form.alertError && <p className="alert alert-danger">{form.alertError}</p>}
<div className={classNames({ 'big-spacer-top': form.alertError !== undefined })}>
{form.renderSpinner()}
{form.renderSubmitButton(
translate('onboarding.create_organization.create_and_upgrade')
)}
</div>
{form.renderTermsOfService()}
</form>
)}
</BillingFormShim>
</div>
);
}
}

export default withCurrentUser(CouponForm);

+ 0
- 57
server/sonar-web/src/main/js/apps/create/organization/PaymentMethodSelect.tsx View File

@@ -1,57 +0,0 @@
/*
* 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 RadioToggle from '../../../components/controls/RadioToggle';
import { translate } from '../../../helpers/l10n';

export enum PaymentMethod {
Card = 'card',
Coupon = 'coupon'
}

interface Props {
onChange: (paymentMethod: PaymentMethod) => void;
paymentMethod: PaymentMethod | undefined;
}

export default class PaymentMethodSelect extends React.PureComponent<Props> {
render() {
const options = Object.values(PaymentMethod).map(value => ({
label: translate('billing', value),
value
}));

return (
<div>
<label className="spacer-bottom">
{translate('onboarding.create_organization.choose_payment_method')}
</label>
<div className="little-spacer-top">
<RadioToggle
name="payment-method"
onCheck={this.props.onChange}
options={options}
value={this.props.paymentMethod}
/>
</div>
</div>
);
}
}

+ 19
- 28
server/sonar-web/src/main/js/apps/create/organization/PlanStep.tsx View File

@@ -18,9 +18,8 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import PaymentMethodSelect, { PaymentMethod } from './PaymentMethodSelect';
import CardForm from './CardForm';
import CouponForm from './CouponForm';
import BillingFormShim from './BillingFormShim';
import { withCurrentUser } from './withCurrentUser';
import PlanSelect, { Plan } from './PlanSelect';
import Step from '../../tutorials/components/Step';
import { translate } from '../../../helpers/l10n';
@@ -29,6 +28,8 @@ import { SubscriptionPlan } from '../../../app/types';
import { SubmitButton } from '../../../components/ui/buttons';
import DeferredSpinner from '../../../components/common/DeferredSpinner';

const BillingForm = withCurrentUser(BillingFormShim);

interface Props {
createOrganization: () => Promise<string>;
deleteOrganization: () => void;
@@ -41,7 +42,6 @@ interface Props {
}

interface State {
paymentMethod?: PaymentMethod;
plan: Plan;
ready: boolean;
submitting: boolean;
@@ -79,10 +79,6 @@ export default class PlanStep extends React.PureComponent<Props, State> {
this.setState({ plan });
};

handlePaymentMethodChange = (paymentMethod: PaymentMethod) => {
this.setState({ paymentMethod });
};

stopSubmitting = () => {
if (this.mounted) {
this.setState({ submitting: false });
@@ -108,27 +104,22 @@ export default class PlanStep extends React.PureComponent<Props, State> {
)}

{this.state.plan === Plan.Paid ? (
<>
<PaymentMethodSelect
onChange={this.handlePaymentMethodChange}
paymentMethod={this.state.paymentMethod}
/>
{this.state.paymentMethod === PaymentMethod.Card && (
<CardForm
createOrganization={this.props.createOrganization}
onFailToUpgrade={this.props.deleteOrganization}
onSubmit={this.props.onPaidPlanChoose}
subscriptionPlans={this.props.subscriptionPlans}
/>
)}
{this.state.paymentMethod === PaymentMethod.Coupon && (
<CouponForm
createOrganization={this.props.createOrganization}
onFailToUpgrade={this.props.deleteOrganization}
onSubmit={this.props.onPaidPlanChoose}
/>
<BillingForm
onCommit={this.props.onPaidPlanChoose}
onFailToUpgrade={this.props.deleteOrganization}
organizationKey={this.props.createOrganization}
subscriptionPlans={this.props.subscriptionPlans}>
{({ onSubmit, renderFormFields, renderSubmitGroup }) => (
<form onSubmit={onSubmit}>
{renderFormFields()}
<div className="billing-input-large big-spacer-top">
{renderSubmitGroup(
translate('onboarding.create_organization.create_and_upgrade')
)}
</div>
</form>
)}
</>
</BillingForm>
) : (
<div className="display-flex-center big-spacer-top">
<SubmitButton disabled={this.state.submitting} onClick={this.handleFreePlanSubmit}>

+ 2
- 15
server/sonar-web/src/main/js/apps/create/organization/__mocks__/BillingFormShim.tsx View File

@@ -24,22 +24,9 @@ export default class BillingFormShim extends React.Component<{ children: any }>
return (
<div id="BillingFormShim">
{this.props.children({
alertError: undefined,
couponValue: '',
onSubmit: jest.fn(),
renderAdditionalInfo: () => <div id="additional-info" />,
renderBillingNameInput: () => <div id="billing-name" />,
renderBraintreeClient: () => <div id="braintree-client" />,
renderCountrySelect: () => <div id="country-select" />,
renderCouponInput: () => <div id="coupon-input" />,
renderEmailInput: () => <div id="email-input" />,
renderNextCharge: () => <div id="next-charge" />,
renderPlanSelect: () => <div id="plan-select" />,
renderResetButton: () => <div id="reset-button" />,
renderSpinner: () => <div id="spinner" />,
renderSubmitButton: () => <div id="submit-button" />,
renderTermsOfService: () => <div id="terms-of-service" />,
renderTypeOfUseSelect: () => <div id="type-of-use-select" />
renderFormFields: () => <div id="form-fields" />,
renderSubmitGroup: () => <div id="submit-group" />
})}
</div>
);

+ 0
- 1
server/sonar-web/src/main/js/apps/create/organization/__tests__/BillingFormShim-test.tsx View File

@@ -38,7 +38,6 @@ it('should render', () => {
shallow(
<BillingFormShim
currentUser={{ isLoggedIn: false }}
onClose={jest.fn()}
onCommit={jest.fn()}
organizationKey="org"
subscriptionPlans={[]}>

+ 0
- 38
server/sonar-web/src/main/js/apps/create/organization/__tests__/CardForm-test.tsx View File

@@ -1,38 +0,0 @@
/*
* 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 { CardForm } from '../CardForm';

jest.mock('../BillingFormShim');

it('should render', () => {
const wrapper = shallow(
<CardForm
createOrganization={jest.fn()}
currentUser={{ isLoggedIn: false }}
onFailToUpgrade={jest.fn()}
onSubmit={jest.fn()}
subscriptionPlans={[{ maxNcloc: 100000, price: 10 }, { maxNcloc: 250000, price: 75 }]}
/>
);
expect(wrapper).toMatchSnapshot();
expect(wrapper.find('BillingFormShim').dive()).toMatchSnapshot();
});

+ 0
- 36
server/sonar-web/src/main/js/apps/create/organization/__tests__/CouponForm-test.tsx View File

@@ -1,36 +0,0 @@
/*
* 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 { CouponForm } from '../CouponForm';

jest.mock('../BillingFormShim');

it('should render', () => {
const wrapper = shallow(
<CouponForm
createOrganization={jest.fn()}
currentUser={{ isLoggedIn: false }}
onFailToUpgrade={jest.fn()}
onSubmit={jest.fn()}
/>
);
expect(wrapper).toMatchSnapshot();
});

+ 0
- 34
server/sonar-web/src/main/js/apps/create/organization/__tests__/PaymentMethodSelect-test.tsx View File

@@ -1,34 +0,0 @@
/*
* 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 PaymentMethodSelect, { PaymentMethod } from '../PaymentMethodSelect';

it('should render and change', () => {
const onChange = jest.fn();
const wrapper = shallow(<PaymentMethodSelect onChange={onChange} paymentMethod={undefined} />);
expect(wrapper).toMatchSnapshot();

wrapper.find('RadioToggle').prop<Function>('onCheck')(PaymentMethod.Card);
expect(onChange).toBeCalledWith(PaymentMethod.Card);

wrapper.setProps({ paymentMethod: PaymentMethod.Card });
expect(wrapper).toMatchSnapshot();
});

+ 3
- 44
server/sonar-web/src/main/js/apps/create/organization/__tests__/PlanStep-test.tsx View File

@@ -22,7 +22,6 @@ import { shallow } from 'enzyme';
import PlanStep from '../PlanStep';
import { waitAndUpdate, click } from '../../../../helpers/testUtils';
import { Plan } from '../PlanSelect';
import { PaymentMethod } from '../PaymentMethodSelect';

jest.mock('../../../../app/components/extensions/utils', () => ({
getExtensionStart: jest.fn().mockResolvedValue(undefined)
@@ -49,7 +48,7 @@ it('should render and use free plan', async () => {
expect(onFreePlanChoose).toBeCalled();
});

it('should upgrade using card', async () => {
it('should upgrade', async () => {
const onPaidPlanChoose = jest.fn();
const wrapper = shallow(
<PlanStep
@@ -72,48 +71,8 @@ it('should upgrade using card', async () => {

wrapper
.dive()
.find('PaymentMethodSelect')
.prop<Function>('onChange')(PaymentMethod.Card);
expect(wrapper.dive()).toMatchSnapshot();

wrapper
.dive()
.find('Connect(withCurrentUser(CardForm))')
.prop<Function>('onSubmit')();
expect(onPaidPlanChoose).toBeCalled();
});

it('should upgrade using coupon', async () => {
const onPaidPlanChoose = jest.fn();
const wrapper = shallow(
<PlanStep
createOrganization={jest.fn().mockResolvedValue('org')}
deleteOrganization={jest.fn().mockResolvedValue(undefined)}
onFreePlanChoose={jest.fn().mockResolvedValue(undefined)}
onPaidPlanChoose={onPaidPlanChoose}
open={true}
startingPrice="10"
subscriptionPlans={[]}
/>
);
await waitAndUpdate(wrapper);

wrapper
.dive()
.find('PlanSelect')
.prop<Function>('onChange')(Plan.Paid);
expect(wrapper.dive()).toMatchSnapshot();

wrapper
.dive()
.find('PaymentMethodSelect')
.prop<Function>('onChange')(PaymentMethod.Coupon);
expect(wrapper.dive()).toMatchSnapshot();

wrapper
.dive()
.find('Connect(withCurrentUser(CouponForm))')
.prop<Function>('onSubmit')();
.find('Connect(withCurrentUser(BillingFormShim))')
.prop<Function>('onCommit')();
expect(onPaidPlanChoose).toBeCalled();
});


+ 0
- 1
server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/BillingFormShim-test.tsx.snap View File

@@ -7,7 +7,6 @@ exports[`should render 1`] = `
"isLoggedIn": false,
}
}
onClose={[MockFunction]}
onCommit={[MockFunction]}
organizationKey="org"
subscriptionPlans={Array []}

+ 0
- 107
server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/CardForm-test.tsx.snap View File

@@ -1,107 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render 1`] = `
<div
className="huge-spacer-top"
>
<BillingFormShim
currentUser={
Object {
"isLoggedIn": false,
}
}
onClose={[Function]}
onCommit={[MockFunction]}
onFailToUpgrade={[MockFunction]}
organizationKey={[MockFunction]}
subscriptionPlans={
Array [
Object {
"maxNcloc": 100000,
"price": 10,
},
Object {
"maxNcloc": 250000,
"price": 75,
},
]
}
/>
</div>
`;

exports[`should render 2`] = `
<div
id="BillingFormShim"
>
<form
onSubmit={[MockFunction]}
>
<div
className="columns column-show-overflow"
>
<div
className="column-half"
>
<h3>
billing.upgrade.billing_info
</h3>
<div
id="email-input"
/>
<div
id="type-of-use-select"
/>
<div
id="billing-name"
/>
<div
id="country-select"
/>
<div
id="additional-info"
/>
</div>
<div
className="column-half"
>
<h3>
billing.upgrade.plan
</h3>
<div
id="plan-select"
/>
<h3>
billing.upgrade.card_info
</h3>
<div
id="braintree-client"
/>
</div>
</div>
<div
className="upgrade-footer big-spacer-top"
>
<div
id="next-charge"
/>
<hr
className="big-spacer-bottom"
/>
</div>
<div
className=""
>
<div
id="spinner"
/>
<div
id="submit-button"
/>
</div>
<div
id="terms-of-service"
/>
</form>
</div>
`;

+ 0
- 22
server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/CouponForm-test.tsx.snap View File

@@ -1,22 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render 1`] = `
<div
className="huge-spacer-top"
>
<BillingFormShim
currentUser={
Object {
"isLoggedIn": false,
}
}
onClose={[Function]}
onCommit={[MockFunction]}
onCouponUpdate={[Function]}
onFailToUpgrade={[MockFunction]}
organizationKey={[MockFunction]}
skipBraintreeInit={true}
subscriptionPlans={Array []}
/>
</div>
`;

+ 0
- 79
server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/PaymentMethodSelect-test.tsx.snap View File

@@ -1,79 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render and change 1`] = `
<div>
<label
className="spacer-bottom"
>
onboarding.create_organization.choose_payment_method
</label>
<div
className="little-spacer-top"
>
<RadioToggle
disabled={false}
name="payment-method"
onCheck={[MockFunction]}
options={
Array [
Object {
"label": "billing.card",
"value": "card",
},
Object {
"label": "billing.coupon",
"value": "coupon",
},
]
}
value={null}
/>
</div>
</div>
`;

exports[`should render and change 2`] = `
<div>
<label
className="spacer-bottom"
>
onboarding.create_organization.choose_payment_method
</label>
<div
className="little-spacer-top"
>
<RadioToggle
disabled={false}
name="payment-method"
onCheck={
[MockFunction] {
"calls": Array [
Array [
"card",
],
],
"results": Array [
Object {
"isThrow": false,
"value": undefined,
},
],
}
}
options={
Array [
Object {
"label": "billing.card",
"value": "card",
},
Object {
"label": "billing.coupon",
"value": "coupon",
},
]
}
value="card"
/>
</div>
</div>
`;

+ 12
- 140
server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/PlanStep-test.tsx.snap View File

@@ -35,11 +35,12 @@ exports[`should preselect paid plan 2`] = `
className="boxed-group-inner"
>
<React.Fragment>
<React.Fragment>
<PaymentMethodSelect
onChange={[Function]}
/>
</React.Fragment>
<Connect(withCurrentUser(BillingFormShim))
onCommit={[MockFunction]}
onFailToUpgrade={[MockFunction]}
organizationKey={[MockFunction]}
subscriptionPlans={Array []}
/>
</React.Fragment>
</div>
</div>
@@ -102,46 +103,7 @@ exports[`should render and use free plan 2`] = `
</div>
`;

exports[`should upgrade using card 1`] = `
<div
className="boxed-group onboarding-step is-open"
>
<div
className="onboarding-step-number"
>
2
</div>
<div
className="boxed-group-header"
>
<h2>
onboarding.create_organization.choose_plan
</h2>
</div>
<div
className=""
>
<div
className="boxed-group-inner"
>
<React.Fragment>
<PlanSelect
onChange={[Function]}
plan="paid"
startingPrice="10"
/>
<React.Fragment>
<PaymentMethodSelect
onChange={[Function]}
/>
</React.Fragment>
</React.Fragment>
</div>
</div>
</div>
`;

exports[`should upgrade using card 2`] = `
exports[`should upgrade 1`] = `
<div
className="boxed-group onboarding-step is-open"
>
@@ -169,102 +131,12 @@ exports[`should upgrade using card 2`] = `
plan="paid"
startingPrice="10"
/>
<React.Fragment>
<PaymentMethodSelect
onChange={[Function]}
paymentMethod="card"
/>
<Connect(withCurrentUser(CardForm))
createOrganization={[MockFunction]}
onFailToUpgrade={[MockFunction]}
onSubmit={[MockFunction]}
subscriptionPlans={Array []}
/>
</React.Fragment>
</React.Fragment>
</div>
</div>
</div>
`;

exports[`should upgrade using coupon 1`] = `
<div
className="boxed-group onboarding-step is-open"
>
<div
className="onboarding-step-number"
>
2
</div>
<div
className="boxed-group-header"
>
<h2>
onboarding.create_organization.choose_plan
</h2>
</div>
<div
className=""
>
<div
className="boxed-group-inner"
>
<React.Fragment>
<PlanSelect
onChange={[Function]}
plan="paid"
startingPrice="10"
/>
<React.Fragment>
<PaymentMethodSelect
onChange={[Function]}
/>
</React.Fragment>
</React.Fragment>
</div>
</div>
</div>
`;

exports[`should upgrade using coupon 2`] = `
<div
className="boxed-group onboarding-step is-open"
>
<div
className="onboarding-step-number"
>
2
</div>
<div
className="boxed-group-header"
>
<h2>
onboarding.create_organization.choose_plan
</h2>
</div>
<div
className=""
>
<div
className="boxed-group-inner"
>
<React.Fragment>
<PlanSelect
onChange={[Function]}
plan="paid"
startingPrice="10"
<Connect(withCurrentUser(BillingFormShim))
onCommit={[MockFunction]}
onFailToUpgrade={[MockFunction]}
organizationKey={[MockFunction]}
subscriptionPlans={Array []}
/>
<React.Fragment>
<PaymentMethodSelect
onChange={[Function]}
paymentMethod="coupon"
/>
<Connect(withCurrentUser(CouponForm))
createOrganization={[MockFunction]}
onFailToUpgrade={[MockFunction]}
onSubmit={[MockFunction]}
/>
</React.Fragment>
</React.Fragment>
</div>
</div>

+ 4
- 0
server/sonar-web/src/main/js/apps/tutorials/styles.css View File

@@ -32,6 +32,10 @@
line-height: var(--controlHeight);
}

.onboarding-step hr {
margin-left: -54px;
}

.onboarding-step-number {
position: absolute;
top: 15px;

+ 7
- 2
server/sonar-web/src/main/js/components/controls/HelpTooltip.tsx View File

@@ -19,7 +19,7 @@
*/
import * as React from 'react';
import * as classNames from 'classnames';
import Tooltip from './Tooltip';
import Tooltip, { Placement } from './Tooltip';
import HelpIcon from '../icons-components/HelpIcon';
import * as theme from '../../app/theme';
import './HelpTooltip.css';
@@ -29,6 +29,7 @@ interface Props {
children?: React.ReactNode;
onShow?: () => void;
overlay: React.ReactNode;
placement?: Placement;
tagName?: string;
}

@@ -38,7 +39,11 @@ export default function HelpTooltip(props: Props) {
return React.createElement(
tagName,
{ className: classNames('help-tooltip', props.className) },
<Tooltip mouseLeaveDelay={0.25} onShow={props.onShow} overlay={props.overlay}>
<Tooltip
mouseLeaveDelay={0.25}
onShow={props.onShow}
overlay={props.overlay}
placement={props.placement}>
<span className="display-inline-flex-center">{children}</span>
</Tooltip>
);

+ 0
- 1
sonar-core/src/main/resources/org/sonar/l10n/core.properties View File

@@ -2736,7 +2736,6 @@ onboarding.create_organization.description=Description
onboarding.create_organization.enter_org_details=Enter your organization details
onboarding.create_organization.enter_payment_details=Enter payment details
onboarding.create_organization.choose_plan=Choose a plan
onboarding.create_organization.choose_payment_method=Choose payment solution
onboarding.create_organization.enter_your_coupon=Enter your coupon
onboarding.create_organization.create_and_upgrade=Create Organization and Upgrade
onboarding.create_organization.ready=All set! Your organization is now ready to go

Loading…
Cancel
Save