Merge pull request #12042 from overleaf/jel-change-plan
[web] Change plan UI now in a modal and update the style GitOrigin-RevId: 1b501b4a972ca676cff32f531862c15c0c8f9e61
This commit is contained in:
+10
-4
@@ -7,10 +7,13 @@ import { CancelSubscriptionButton } from './cancel-subscription-button'
|
||||
import { CancelSubscription } from './cancel-plan/cancel-subscription'
|
||||
import { PendingPlanChange } from './pending-plan-change'
|
||||
import { TrialEnding } from './trial-ending'
|
||||
import { ChangePlan } from './change-plan/change-plan'
|
||||
import { PendingAdditionalLicenses } from './pending-additional-licenses'
|
||||
import { ContactSupportToChangeGroupPlan } from './contact-support-to-change-group-plan'
|
||||
import isInFreeTrial from '../../../../util/is-in-free-trial'
|
||||
import { ChangePlanModal } from './change-plan/modals/change-plan-modal'
|
||||
import { ConfirmChangePlanModal } from './change-plan/modals/confirm-change-plan-modal'
|
||||
import { KeepCurrentPlanModal } from './change-plan/modals/keep-current-plan-modal'
|
||||
import { ChangeToGroupModal } from './change-plan/modals/change-to-group-modal'
|
||||
|
||||
export function ActiveSubscription({
|
||||
subscription,
|
||||
@@ -18,7 +21,7 @@ export function ActiveSubscription({
|
||||
subscription: RecurlySubscription
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
const { recurlyLoadError, setShowChangePersonalPlan, showCancellation } =
|
||||
const { recurlyLoadError, setModalIdShown, showCancellation } =
|
||||
useSubscriptionDashboardContext()
|
||||
|
||||
if (showCancellation) return <CancelSubscription />
|
||||
@@ -59,7 +62,7 @@ export function ActiveSubscription({
|
||||
{' '}
|
||||
<button
|
||||
className="btn-inline-link"
|
||||
onClick={() => setShowChangePersonalPlan(true)}
|
||||
onClick={() => setModalIdShown('change-plan')}
|
||||
>
|
||||
{t('change_plan')}
|
||||
</button>
|
||||
@@ -120,7 +123,10 @@ export function ActiveSubscription({
|
||||
<CancelSubscriptionButton subscription={subscription} />
|
||||
)}
|
||||
|
||||
<ChangePlan />
|
||||
<ChangePlanModal />
|
||||
<ConfirmChangePlanModal />
|
||||
<KeepCurrentPlanModal />
|
||||
<ChangeToGroupModal />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
-40
@@ -1,40 +0,0 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import LoadingSpinner from '../../../../../../../shared/components/loading-spinner'
|
||||
import { useSubscriptionDashboardContext } from '../../../../../context/subscription-dashboard-context'
|
||||
import { ChangeToGroupPlan } from './change-to-group-plan'
|
||||
import { ConfirmChangePlanModal } from './modals/confirm-change-plan-modal'
|
||||
import { IndividualPlansTable } from './individual-plans-table'
|
||||
import { KeepCurrentPlanModal } from './modals/keep-current-plan-modal'
|
||||
import { ChangeToGroupModal } from './modals/change-to-group-modal'
|
||||
|
||||
export function ChangePlan() {
|
||||
const { t } = useTranslation()
|
||||
const {
|
||||
plans,
|
||||
queryingIndividualPlansData,
|
||||
recurlyLoadError,
|
||||
showChangePersonalPlan,
|
||||
} = useSubscriptionDashboardContext()
|
||||
|
||||
if (!showChangePersonalPlan || !plans || recurlyLoadError) return null
|
||||
|
||||
if (queryingIndividualPlansData) {
|
||||
return (
|
||||
<>
|
||||
<h2>{t('change_plan')}</h2>
|
||||
<LoadingSpinner />
|
||||
</>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
<h2>{t('change_plan')}</h2>
|
||||
<IndividualPlansTable plans={plans} />
|
||||
<ChangeToGroupPlan />
|
||||
<ConfirmChangePlanModal />
|
||||
<KeepCurrentPlanModal />
|
||||
<ChangeToGroupModal />
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -10,13 +10,13 @@ export function ChangeToGroupPlan() {
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2>{t('looking_multiple_licenses')}</h2>
|
||||
<div className="card-gray text-center mt-3">
|
||||
<h2 style={{ marginTop: 0 }}>{t('looking_multiple_licenses')}</h2>
|
||||
<p style={{ margin: 0 }}>{t('reduce_costs_group_licenses')}</p>
|
||||
<br />
|
||||
<button className="btn btn-primary" onClick={handleClick}>
|
||||
{t('change_to_group_plan')}
|
||||
</button>
|
||||
</>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
+2
-3
@@ -92,10 +92,9 @@ function PlansRows({ plans }: { plans: Array<Plan> }) {
|
||||
|
||||
export function IndividualPlansTable({ plans }: { plans: Array<Plan> }) {
|
||||
const { t } = useTranslation()
|
||||
const { recurlyLoadError, showChangePersonalPlan } =
|
||||
useSubscriptionDashboardContext()
|
||||
const { recurlyLoadError } = useSubscriptionDashboardContext()
|
||||
|
||||
if (!showChangePersonalPlan || !plans || recurlyLoadError) return null
|
||||
if (!plans || recurlyLoadError) return null
|
||||
|
||||
return (
|
||||
<table className="table table-vertically-centered-cells">
|
||||
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
import { Modal } from 'react-bootstrap'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { SubscriptionDashModalIds } from '../../../../../../../../../../types/subscription/dashboard/modal-ids'
|
||||
import AccessibleModal from '../../../../../../../../shared/components/accessible-modal'
|
||||
import LoadingSpinner from '../../../../../../../../shared/components/loading-spinner'
|
||||
import { useSubscriptionDashboardContext } from '../../../../../../context/subscription-dashboard-context'
|
||||
import { ChangeToGroupPlan } from '../change-to-group-plan'
|
||||
import { IndividualPlansTable } from '../individual-plans-table'
|
||||
|
||||
function ChangePlanOptions() {
|
||||
const { plans, queryingIndividualPlansData, recurlyLoadError } =
|
||||
useSubscriptionDashboardContext()
|
||||
|
||||
if (!plans || recurlyLoadError) return null
|
||||
|
||||
if (queryingIndividualPlansData) {
|
||||
return (
|
||||
<>
|
||||
<LoadingSpinner />
|
||||
</>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
<div className="table-outlined-container">
|
||||
<IndividualPlansTable plans={plans} />
|
||||
</div>
|
||||
<ChangeToGroupPlan />
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export function ChangePlanModal() {
|
||||
const modalId: SubscriptionDashModalIds = 'change-plan'
|
||||
const { t } = useTranslation()
|
||||
const { handleCloseModal, modalIdShown } = useSubscriptionDashboardContext()
|
||||
|
||||
if (modalIdShown !== modalId) return null
|
||||
|
||||
return (
|
||||
<AccessibleModal id={modalId} show animation onHide={handleCloseModal}>
|
||||
<Modal.Header closeButton>
|
||||
<Modal.Title>{t('change_plan')}</Modal.Title>
|
||||
</Modal.Header>
|
||||
|
||||
<Modal.Body>
|
||||
<ChangePlanOptions />
|
||||
</Modal.Body>
|
||||
</AccessibleModal>
|
||||
)
|
||||
}
|
||||
-7
@@ -66,8 +66,6 @@ type SubscriptionDashboardContextValue = {
|
||||
setRecurlyLoadError: React.Dispatch<React.SetStateAction<boolean>>
|
||||
showCancellation: boolean
|
||||
setShowCancellation: React.Dispatch<React.SetStateAction<boolean>>
|
||||
showChangePersonalPlan: boolean
|
||||
setShowChangePersonalPlan: React.Dispatch<React.SetStateAction<boolean>>
|
||||
leavingGroupId?: string
|
||||
setLeavingGroupId: React.Dispatch<React.SetStateAction<string | undefined>>
|
||||
}
|
||||
@@ -86,7 +84,6 @@ export function SubscriptionDashboardProvider({
|
||||
>()
|
||||
const [recurlyLoadError, setRecurlyLoadError] = useState(false)
|
||||
const [showCancellation, setShowCancellation] = useState(false)
|
||||
const [showChangePersonalPlan, setShowChangePersonalPlan] = useState(false)
|
||||
const [plans, setPlans] = useState([])
|
||||
const [queryingIndividualPlansData, setQueryingIndividualPlansData] =
|
||||
useState(true)
|
||||
@@ -268,8 +265,6 @@ export function SubscriptionDashboardProvider({
|
||||
setRecurlyLoadError,
|
||||
showCancellation,
|
||||
setShowCancellation,
|
||||
showChangePersonalPlan,
|
||||
setShowChangePersonalPlan,
|
||||
leavingGroupId,
|
||||
setLeavingGroupId,
|
||||
}),
|
||||
@@ -304,8 +299,6 @@ export function SubscriptionDashboardProvider({
|
||||
setRecurlyLoadError,
|
||||
showCancellation,
|
||||
setShowCancellation,
|
||||
showChangePersonalPlan,
|
||||
setShowChangePersonalPlan,
|
||||
leavingGroupId,
|
||||
setLeavingGroupId,
|
||||
]
|
||||
|
||||
@@ -578,3 +578,67 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Subscription Dash
|
||||
*/
|
||||
|
||||
#change-plan {
|
||||
.modal-dialog {
|
||||
&:extend(.modal-lg);
|
||||
}
|
||||
|
||||
table {
|
||||
@media only screen and (min-width: @screen-md-min) {
|
||||
th:last-child,
|
||||
td:last-child {
|
||||
width: 1%; // will expand to fit the content
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: @screen-sm-max) {
|
||||
display: block;
|
||||
|
||||
thead {
|
||||
display: none;
|
||||
}
|
||||
|
||||
tbody,
|
||||
td,
|
||||
tr {
|
||||
display: inline-block;
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
td:first-child {
|
||||
padding-top: @padding-md;
|
||||
}
|
||||
td:last-child {
|
||||
padding-top: @padding-sm;
|
||||
padding-bottom: @padding-md;
|
||||
}
|
||||
|
||||
tr:first-child {
|
||||
td:first-child {
|
||||
padding-top: @padding-sm;
|
||||
}
|
||||
}
|
||||
|
||||
tr {
|
||||
border-bottom: 1px solid @table-border-color;
|
||||
td,
|
||||
th {
|
||||
border: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
tr:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,3 +65,9 @@
|
||||
.card-content {
|
||||
padding: @padding-md;
|
||||
}
|
||||
|
||||
.card-gray {
|
||||
&:extend(.card);
|
||||
background-color: @neutral-10;
|
||||
border-radius: @card-border-radius;
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ th {
|
||||
}
|
||||
}
|
||||
|
||||
// Bordered version
|
||||
// Bordered versions
|
||||
//
|
||||
// Add borders all around the table and between all the columns.
|
||||
|
||||
@@ -233,3 +233,13 @@ table {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.table-outlined-container {
|
||||
border: @border-width-sm solid @table-border-color;
|
||||
padding: @padding-sm @padding-sm 0 @padding-sm;
|
||||
border-radius: @border-radius-base-new;
|
||||
|
||||
table {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,8 +165,10 @@
|
||||
@line-height-small: 1.5;
|
||||
|
||||
@border-radius-base: 3px;
|
||||
@border-radius-base-new: 4px;
|
||||
@border-radius-large: 5px;
|
||||
@border-radius-small: 2px;
|
||||
@border-width-sm: 1px;
|
||||
@border-width-base: 3px;
|
||||
@border-color-base: @ol-blue-gray-2;
|
||||
|
||||
@@ -932,6 +934,7 @@
|
||||
|
||||
// Cards
|
||||
@card-box-shadow: none;
|
||||
@card-border-radius: @border-radius-base-new;
|
||||
|
||||
// Project table
|
||||
@structured-list-link-color: @ol-blue;
|
||||
|
||||
@@ -107,8 +107,10 @@
|
||||
@line-height-small: 1.5;
|
||||
|
||||
@border-radius-base: 3px;
|
||||
@border-radius-base-new: 4px;
|
||||
@border-radius-large: 5px;
|
||||
@border-radius-small: 2px;
|
||||
@border-width-sm: 1px;
|
||||
@border-width-base: 3px; // only used by plans and cards
|
||||
@border-size: 1px;
|
||||
@border-color-base: @neutral-60;
|
||||
@@ -720,6 +722,7 @@
|
||||
|
||||
// Cards
|
||||
@card-box-shadow: none;
|
||||
@card-border-radius: @border-radius-base-new;
|
||||
|
||||
// Project table
|
||||
@structured-list-link-color: @blue;
|
||||
|
||||
+1
-11
@@ -1,7 +1,6 @@
|
||||
import { expect } from 'chai'
|
||||
import { fireEvent, screen, waitFor, within } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import { ChangePlan } from '../../../../../../../../../frontend/js/features/subscription/components/dashboard/states/active/change-plan/change-plan'
|
||||
import { groupPlans, plans } from '../../../../../fixtures/plans'
|
||||
import {
|
||||
annualActiveSubscription,
|
||||
@@ -22,7 +21,7 @@ import {
|
||||
} from '../../../../../../../../../frontend/js/features/subscription/data/subscription-url'
|
||||
import { renderActiveSubscription } from '../../../../../helpers/render-active-subscription'
|
||||
|
||||
describe('<ChangePlan />', function () {
|
||||
describe('<ChangePlanModal />', function () {
|
||||
let reloadStub: () => void
|
||||
const originalLocation = window.location
|
||||
const plansMetaTag = { name: 'ol-plans', value: plans }
|
||||
@@ -42,15 +41,6 @@ describe('<ChangePlan />', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('does not render the UI when showChangePersonalPlan is false', function () {
|
||||
window.metaAttributesCache.delete('ol-plans')
|
||||
const { container } = renderWithSubscriptionDashContext(<ChangePlan />, {
|
||||
metaTags: [plansMetaTag],
|
||||
})
|
||||
|
||||
expect(container.firstChild).to.be.null
|
||||
})
|
||||
|
||||
it('renders the individual plans table and group plans UI', async function () {
|
||||
renderActiveSubscription(annualActiveSubscription)
|
||||
const button = screen.getByRole('button', { name: 'Change plan' })
|
||||
|
||||
@@ -3,3 +3,4 @@ export type SubscriptionDashModalIds =
|
||||
| 'change-to-group'
|
||||
| 'keep-current-plan'
|
||||
| 'leave-group'
|
||||
| 'change-plan'
|
||||
|
||||
Reference in New Issue
Block a user