Skip to content

Commit 4ec0a46

Browse files
authored
feat: Add personal org warnings to checkout flow (#3896)
1 parent 8936517 commit 4ec0a46

25 files changed

+984
-1103
lines changed

src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/ProPlanController/BillingOptions/BillingOptions.test.tsx

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -153,15 +153,13 @@ describe('BillingOptions', () => {
153153
}
154154
)
155155

156-
const annualBtn = await screen.findByRole('button', { name: 'Annual' })
156+
const annualBtn = await screen.findByTestId('radio-annual')
157157
expect(annualBtn).toBeInTheDocument()
158-
await waitFor(() => expect(annualBtn).toHaveClass('bg-ds-primary-base'))
158+
await waitFor(() => expect(annualBtn).toBeChecked())
159159

160-
const monthlyBtn = await screen.findByRole('button', {
161-
name: 'Monthly',
162-
})
160+
const monthlyBtn = await screen.findByTestId('radio-monthly')
163161
expect(monthlyBtn).toBeInTheDocument()
164-
expect(monthlyBtn).not.toHaveClass('bg-ds-primary-base')
162+
expect(monthlyBtn).not.toBeChecked()
165163
})
166164

167165
it('renders annual pricing scheme', async () => {
@@ -200,9 +198,7 @@ describe('BillingOptions', () => {
200198
}
201199
)
202200

203-
const monthlyBtn = await screen.findByRole('button', {
204-
name: 'Monthly',
205-
})
201+
const monthlyBtn = await screen.findByTestId('radio-monthly')
206202
expect(monthlyBtn).toBeInTheDocument()
207203
await user.click(monthlyBtn)
208204

@@ -230,15 +226,13 @@ describe('BillingOptions', () => {
230226
}
231227
)
232228

233-
const annualBtn = await screen.findByRole('button', { name: 'Annual' })
229+
const annualBtn = await screen.findByTestId('radio-annual')
234230
expect(annualBtn).toBeInTheDocument()
235-
expect(annualBtn).not.toHaveClass('bg-ds-primary-base')
231+
expect(annualBtn).not.toBeChecked()
236232

237-
const monthlyBtn = await screen.findByRole('button', {
238-
name: 'Monthly',
239-
})
233+
const monthlyBtn = await screen.findByTestId('radio-monthly')
240234
expect(monthlyBtn).toBeInTheDocument()
241-
expect(monthlyBtn).toHaveClass('bg-ds-primary-base')
235+
expect(monthlyBtn).toBeChecked()
242236
})
243237

244238
it('renders correct pricing scheme', async () => {
@@ -277,9 +271,7 @@ describe('BillingOptions', () => {
277271
}
278272
)
279273

280-
const annualBtn = await screen.findByRole('button', {
281-
name: 'Annual',
282-
})
274+
const annualBtn = await screen.findByTestId('radio-annual')
283275
expect(annualBtn).toBeInTheDocument()
284276
await user.click(annualBtn)
285277

src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/ProPlanController/BillingOptions/BillingOptions.tsx

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
} from 'services/account/useAvailablePlans'
99
import { usePlanData } from 'services/account/usePlanData'
1010
import { BillingRate, findProPlans } from 'shared/utils/billing'
11-
import { OptionButton } from 'ui/OptionButton/OptionButton'
11+
import { RadioTileGroup } from 'ui/RadioTileGroup'
1212

1313
import { OptionPeriod, TimePeriods } from '../../../constants'
1414
import { UpgradeFormFields } from '../../../UpgradeForm'
@@ -64,31 +64,35 @@ const BillingControls: React.FC<BillingControlsProps> = ({
6464

6565
return (
6666
<div className="flex w-fit flex-col gap-2">
67-
<h3 className="font-semibold">Choose a billing cycle</h3>
67+
<h3 className="font-semibold">Step 2: Choose a billing cycle</h3>
6868
<div className="inline-flex items-center gap-2">
69-
<OptionButton
70-
type="button"
71-
active={option}
72-
onChange={({ text }) => {
73-
if (text === TimePeriods.ANNUAL) {
69+
<RadioTileGroup
70+
value={option}
71+
onValueChange={(value: OptionPeriod) => {
72+
if (value === TimePeriods.ANNUAL) {
7473
setFormValue('newPlan', proPlanYear)
7574
} else {
7675
setFormValue('newPlan', proPlanMonth)
7776
}
7877

79-
setOption(text)
78+
setOption(value)
8079
}}
81-
options={
82-
[
83-
{
84-
text: TimePeriods.ANNUAL,
85-
},
86-
{
87-
text: TimePeriods.MONTHLY,
88-
},
89-
] as const
90-
}
91-
/>
80+
>
81+
<RadioTileGroup.Item
82+
value={TimePeriods.ANNUAL}
83+
className="w-32"
84+
data-testid="radio-annual"
85+
>
86+
<RadioTileGroup.Label>{TimePeriods.ANNUAL}</RadioTileGroup.Label>
87+
</RadioTileGroup.Item>
88+
<RadioTileGroup.Item
89+
value={TimePeriods.MONTHLY}
90+
className="w-32"
91+
data-testid="radio-monthly"
92+
>
93+
<RadioTileGroup.Label>{TimePeriods.MONTHLY}</RadioTileGroup.Label>
94+
</RadioTileGroup.Item>
95+
</RadioTileGroup>
9296
<p>
9397
<span className="font-semibold">${baseUnitPrice}</span> per
9498
seat/month, billed {billingRate}

src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/ProPlanController/ProPlanController.test.tsx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -278,25 +278,25 @@ describe('ProPlanController', () => {
278278
setup({ planValue: proPlanMonth })
279279
render(<ProPlanController {...props} />, { wrapper: wrapper() })
280280

281-
const optionBtn = await screen.findByRole('button', { name: 'Monthly' })
281+
const optionBtn = await screen.findByTestId('radio-monthly')
282282
expect(optionBtn).toBeInTheDocument()
283283
})
284284

285285
it('renders annual option button', async () => {
286286
setup({ planValue: proPlanMonth })
287287
render(<ProPlanController {...props} />, { wrapper: wrapper() })
288288

289-
const optionBtn = await screen.findByRole('button', { name: 'Annual' })
289+
const optionBtn = await screen.findByTestId('radio-annual')
290290
expect(optionBtn).toBeInTheDocument()
291291
})
292292

293293
it('renders monthly option button as "selected"', async () => {
294294
setup({ planValue: proPlanMonth })
295295
render(<ProPlanController {...props} />, { wrapper: wrapper() })
296296

297-
const optionBtn = await screen.findByRole('button', { name: 'Monthly' })
297+
const optionBtn = await screen.findByTestId('radio-monthly')
298298
expect(optionBtn).toBeInTheDocument()
299-
expect(optionBtn).toHaveClass('bg-ds-primary-base')
299+
expect(optionBtn).toBeChecked()
300300
})
301301

302302
it('has the price for the year', async () => {
@@ -321,25 +321,25 @@ describe('ProPlanController', () => {
321321
setup({ planValue: proPlanYear, monthlyPlan: false })
322322
render(<ProPlanController {...props} />, { wrapper: wrapper() })
323323

324-
const optionBtn = await screen.findByRole('button', { name: 'Monthly' })
324+
const optionBtn = await screen.findByTestId('radio-monthly')
325325
expect(optionBtn).toBeInTheDocument()
326326
})
327327

328328
it('renders annual option button', async () => {
329329
setup({ planValue: proPlanYear, monthlyPlan: false })
330330
render(<ProPlanController {...props} />, { wrapper: wrapper() })
331331

332-
const optionBtn = await screen.findByRole('button', { name: 'Annual' })
332+
const optionBtn = await screen.findByTestId('radio-annual')
333333
expect(optionBtn).toBeInTheDocument()
334334
})
335335

336336
it('renders annual option button as "selected"', async () => {
337337
setup({ planValue: proPlanYear, monthlyPlan: false })
338338
render(<ProPlanController {...props} />, { wrapper: wrapper() })
339339

340-
const optionBtn = await screen.findByRole('button', { name: 'Annual' })
340+
const optionBtn = await screen.findByTestId('radio-annual')
341341
expect(optionBtn).toBeInTheDocument()
342-
expect(optionBtn).toHaveClass('bg-ds-primary-base')
342+
expect(optionBtn).toBeChecked()
343343
})
344344

345345
it('has the price for the year', async () => {

src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/ProPlanController/ProPlanController.tsx

Lines changed: 38 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { UseFormRegister, UseFormSetValue } from 'react-hook-form'
22

33
import { IndividualPlan } from 'services/account/useAvailablePlans'
44
import { MIN_NB_SEATS_PRO } from 'shared/utils/upgradeForm'
5+
import { Card } from 'ui/Card'
56
import TextInput from 'ui/TextInput'
67

78
import BillingOptions from './BillingOptions'
@@ -32,34 +33,44 @@ const ProPlanController: React.FC<ProPlanControllerProps> = ({
3233
}) => {
3334
return (
3435
<>
35-
<div className="flex flex-col gap-2">
36-
<BillingOptions newPlan={newPlan} setFormValue={setFormValue} />
37-
</div>
38-
<div className="flex flex-col gap-2 xl:w-5/12">
39-
<div className="w-1/2">
40-
<TextInput
41-
data-cy="seats"
42-
dataMarketing="plan-pricing-seats"
43-
{...register('seats')}
44-
id="nb-seats"
45-
size={20}
46-
type="number"
47-
label="Enter seat count"
48-
min={MIN_NB_SEATS_PRO}
49-
/>
36+
<Card.Content>
37+
<div className="flex flex-col gap-2">
38+
<BillingOptions newPlan={newPlan} setFormValue={setFormValue} />
5039
</div>
51-
<UserCount />
52-
</div>
53-
<PriceCallout
54-
seats={seats}
55-
newPlan={newPlan}
56-
setFormValue={setFormValue}
57-
/>
58-
{errors?.seats && (
59-
<p className="rounded-md bg-ds-error-quinary p-3 text-ds-error-nonary">
60-
{errors?.seats?.message}
61-
</p>
62-
)}
40+
</Card.Content>
41+
<hr />
42+
{/* if you're reading this, I'm sorry */}
43+
<Card.Content>
44+
<div className="flex flex-col gap-2 xl:w-5/12">
45+
<label htmlFor="nb-seats" className="font-semibold">
46+
Step 3: Enter seat count
47+
</label>
48+
<div className="w-1/4">
49+
<TextInput
50+
data-cy="seats"
51+
dataMarketing="plan-pricing-seats"
52+
{...register('seats')}
53+
id="nb-seats"
54+
size={20}
55+
type="number"
56+
min={MIN_NB_SEATS_PRO}
57+
/>
58+
</div>
59+
<UserCount />
60+
</div>
61+
</Card.Content>
62+
<Card.Content>
63+
<PriceCallout
64+
seats={seats}
65+
newPlan={newPlan}
66+
setFormValue={setFormValue}
67+
/>
68+
{errors?.seats && (
69+
<p className="rounded-md bg-ds-error-quinary p-3 text-ds-error-nonary">
70+
{errors?.seats?.message}
71+
</p>
72+
)}
73+
</Card.Content>
6374
</>
6475
)
6576
}

src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/ProPlanController/UserCount/UserCount.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,11 @@ const UserText: React.FC<UserTextProps> = ({
3434
activatedUserCount,
3535
inactiveUserCount,
3636
}) => {
37+
const hasOneMember = activatedUserCount + inactiveUserCount === 1
3738
return (
3839
<p>
39-
Your organization has {activatedUserCount + inactiveUserCount} members.
40+
Your organization has {activatedUserCount + inactiveUserCount} member
41+
{hasOneMember ? '' : 's'}.
4042
</p>
4143
)
4244
}

src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/SentryPlanController/BillingOptions/BillingOptions.test.tsx

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -144,17 +144,13 @@ describe('BillingOptions', () => {
144144
}
145145
)
146146

147-
const annualBtn = await screen.findByRole('button', { name: 'Annual' })
147+
const annualBtn = await screen.findByTestId('radio-annual')
148148
expect(annualBtn).toBeInTheDocument()
149-
await waitFor(() => expect(annualBtn).toHaveClass('bg-ds-primary-base'))
149+
await waitFor(() => expect(annualBtn).toBeChecked())
150150

151-
const monthlyBtn = await screen.findByRole('button', {
152-
name: 'Monthly',
153-
})
151+
const monthlyBtn = await screen.findByTestId('radio-monthly')
154152
expect(monthlyBtn).toBeInTheDocument()
155-
await waitFor(() =>
156-
expect(monthlyBtn).not.toHaveClass('bg-ds-primary-base')
157-
)
153+
await waitFor(() => expect(monthlyBtn).not.toBeChecked())
158154
})
159155

160156
it('renders annual pricing scheme', async () => {
@@ -193,9 +189,7 @@ describe('BillingOptions', () => {
193189
}
194190
)
195191

196-
const monthlyBtn = await screen.findByRole('button', {
197-
name: 'Monthly',
198-
})
192+
const monthlyBtn = await screen.findByTestId('radio-monthly')
199193
expect(monthlyBtn).toBeInTheDocument()
200194
await user.click(monthlyBtn)
201195

@@ -223,15 +217,13 @@ describe('BillingOptions', () => {
223217
}
224218
)
225219

226-
const annualBtn = await screen.findByRole('button', { name: 'Annual' })
220+
const annualBtn = await screen.findByTestId('radio-annual')
227221
expect(annualBtn).toBeInTheDocument()
228-
expect(annualBtn).not.toHaveClass('bg-ds-primary-base')
222+
expect(annualBtn).not.toBeChecked()
229223

230-
const monthlyBtn = await screen.findByRole('button', {
231-
name: 'Monthly',
232-
})
224+
const monthlyBtn = await screen.findByTestId('radio-monthly')
233225
expect(monthlyBtn).toBeInTheDocument()
234-
expect(monthlyBtn).toHaveClass('bg-ds-primary-base')
226+
expect(monthlyBtn).toBeChecked()
235227
})
236228

237229
it('renders correct pricing scheme', async () => {
@@ -270,9 +262,7 @@ describe('BillingOptions', () => {
270262
}
271263
)
272264

273-
const annualBtn = await screen.findByRole('button', {
274-
name: 'Annual',
275-
})
265+
const annualBtn = await screen.findByTestId('radio-annual')
276266
expect(annualBtn).toBeInTheDocument()
277267
await user.click(annualBtn)
278268

0 commit comments

Comments
 (0)