Skip to content

Commit

Permalink
feat: Paid plans activation required banner (#2832)
Browse files Browse the repository at this point in the history
* feat: Add activation banner for trial eligible owners

* pull out interface + spec stuff

* Update to reflect paid plan activation banner

* Refactor CircleCI repo onboarding into one file (#2806)

* Refactor Other CI repo onboarding into one file (#2807)

* Update repo onboarding title position and page alignment (#2818)

* sec: 390 - Add validation for potential XSS vuln (#2797)

* add tests, and validation for provider

* add back supportServiceless param

* ref: 1548 Part 1: Convert all Header files to TS (#2821)

* ref all header files to TS

* remove prop types and rebase

* fix: Remove repository from GUT settings page header (#2823)

Small tweak removing `repository` from the GUT settings page.

* Install radix-ui react radio group (#2825)

* Update repo onboarding steps with new Card component (#2819)

GH codecov/engineering-team#1665

* feat: Add hasSeatsLeft to plan query

* Update to reflect SeatsLimitReachedBanner

* feat: paid plan activation banner

* update with from FreePlanSeatsLimitBanner

* value duplicate

* feat: Activation required banner

* clean up previous commit

* match design

* fix style

* Resolve conflicts

---------

Co-authored-by: Spencer Murray <[email protected]>
Co-authored-by: ajay-sentry <[email protected]>
Co-authored-by: nicholas-codecov <[email protected]>
  • Loading branch information
4 people authored May 7, 2024
1 parent 0247fbe commit ce7fe0e
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { MemoryRouter, Route } from 'react-router-dom'
import ActivationBanner from './ActivationBanner'

jest.mock('./TrialEligibleBanner', () => () => 'TrialEligibleBanner')
jest.mock('./ActivationRequiredBanner', () => () => 'ActivationRequiredBanner')
jest.mock('./FreePlanSeatsLimitBanner', () => () => 'FreePlanSeatsLimitBanner')
jest.mock('./PaidPlanSeatsLimitBanner', () => () => 'PaidPlanSeatsLimitBanner')

Expand Down Expand Up @@ -102,6 +103,16 @@ describe('ActivationBanner', () => {
expect(container).toBeEmptyDOMElement()
})

it('renders activation required banner if user is not on free plan and has seats left', async () => {
setup(true, 'ONGOING', 'users-pro', true)
render(<ActivationBanner />, { wrapper })

const ActivationRequiredBanner = await screen.findByText(
/ActivationRequiredBanner/
)
expect(ActivationRequiredBanner).toBeInTheDocument()
})

it('renders seats limit reached banner if user has no seats left and on free plan', async () => {
setup(true, 'ONGOING', 'users-basic', false)
render(<ActivationBanner />, { wrapper })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useParams } from 'react-router-dom'
import { TrialStatuses, usePlanData } from 'services/account'
import { isBasicPlan, isFreePlan } from 'shared/utils/billing'

import ActivationRequiredBanner from './ActivationRequiredBanner'
import FreePlanSeatsLimitBanner from './FreePlanSeatsLimitBanner'
import PaidPlanSeatsLimitBanner from './PaidPlanSeatsLimitBanner'
import TrialEligibleBanner from './TrialEligibleBanner'
Expand Down Expand Up @@ -30,6 +31,10 @@ function ActivationBanner() {
return <TrialEligibleBanner />
}

if (!seatsLimitReached && !isFreePlanValue) {
return <ActivationRequiredBanner />
}

if (seatsLimitReached && isFreePlanValue) {
return <FreePlanSeatsLimitBanner />
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { render, screen } from '@testing-library/react'
import { MemoryRouter, Route } from 'react-router-dom'

import ActivationRequiredBanner from './ActivationRequiredBanner'

const wrapper: React.FC<React.PropsWithChildren> = ({ children }) => (
<MemoryRouter initialEntries={['/gh/codecov/gazebo/new']}>
<Route path="/:provider/:owner/:repo/new">{children}</Route>
</MemoryRouter>
)

describe('ActivationRequiredBanner', () => {
it('renders the banner with correct content', () => {
render(<ActivationRequiredBanner />, { wrapper })

const bannerHeading = screen.getByRole('heading', {
name: /Activation Required/,
})
expect(bannerHeading).toBeInTheDocument()

const description = screen.getByText(
/You have available seats, but activation is needed./
)
expect(description).toBeInTheDocument()
})

it('renders correct links', () => {
render(<ActivationRequiredBanner />, { wrapper })

const manageMembersLink = screen.getByRole('link', {
name: /Manage Members/,
})
expect(manageMembersLink).toBeInTheDocument()
expect(manageMembersLink).toHaveAttribute('href', '/members/gh/codecov')
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import Banner from 'ui/Banner'
import BannerContent from 'ui/Banner/BannerContent'
import BannerHeading from 'ui/Banner/BannerHeading'
import Button from 'ui/Button'

function ActivationRequiredBanner() {
return (
<Banner variant="plain">
<BannerContent>
<BannerHeading>
<h2 className="font-semibold">&#8505; Activation Required</h2>
<div className="left-[100px] md:relative">
<Button
hook="trial-eligible-banner-start-trial"
to={{
pageName: 'membersTab',
}}
disabled={false}
variant="primary"
>
Manage Members
</Button>
</div>
</BannerHeading>
<p>You have available seats, but activation is needed.</p>
</BannerContent>
</Banner>
)
}

export default ActivationRequiredBanner
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './ActivationRequiredBanner'

0 comments on commit ce7fe0e

Please sign in to comment.