Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8acc0ed
Implements upsell interactions
Jun 5, 2026
46a2256
Merge branch 'main' into jr/extra-accounts-interactions
j-ruda-guardian Jun 5, 2026
e504f80
Implements improved form validatio
Jun 8, 2026
1b8cd24
Merge branch 'main' into jr/extra-accounts-interactions
j-ruda-guardian Jun 16, 2026
001dc30
Implements mobile and toast component
Jun 18, 2026
9815751
Merge branch 'main' into jr/extra-accounts-interactions
j-ruda-guardian Jun 22, 2026
f1bc829
Merge branch 'main' into jr/extra-accounts-interactions
j-ruda-guardian Jun 22, 2026
99fa9c7
Implements modals and actions
Jun 23, 2026
3087ca1
Merge branch 'main' into jr/extra-accounts-interactions
j-ruda-guardian Jun 23, 2026
1a172f4
Merge branch 'main' into jr/extra-accounts-interactions
j-ruda-guardian Jun 24, 2026
3715b3a
Merge branch 'main' into jr/extra-accounts-interactions
j-ruda-guardian Jun 24, 2026
9e98cc1
Merge branch 'main' into jr/extra-accounts-interactions
j-ruda-guardian Jun 26, 2026
f1cd343
Implements API calls
Jun 24, 2026
4c3cef5
mma primary endpoint boiler
Jun 29, 2026
3c3fbee
Merge branch 'main' into jr/extra-accounts-interactions
j-ruda-guardian Jul 2, 2026
36c2896
merge main into current
Jul 6, 2026
4c2cfa1
Merge branch 'main' into jr/extra-accounts-interactions
j-ruda-guardian Jul 7, 2026
9cc2fa4
Merge branch 'main' into jr/extra-accounts-interactions
j-ruda-guardian Jul 8, 2026
ce66a60
stories and small sizing fixes
Jul 8, 2026
d8378b8
Merge branch 'main' into jr/extra-accounts-interactions
j-ruda-guardian Jul 15, 2026
02b4b2f
Remove access integration
Jul 15, 2026
43c045c
Merge branch 'main' into jr/extra-accounts-interactions
j-ruda-guardian Jul 15, 2026
7fc8596
Toast styling
Jul 16, 2026
f3bfd0c
Merge branch 'main' into jr/extra-accounts-interactions
j-ruda-guardian Jul 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions client/components/mma/MMAPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ const AccountOverview = lazyWithRetry(() =>
).then(({ AccountOverview }) => ({ default: AccountOverview })),
);

const ExtraAccounts = lazyWithRetry(() =>
import(
/* webpackChunkName: "ExtraAccounts" */ './extraAccounts/ExtraAccounts'
).then(({ ExtraAccounts }) => ({ default: ExtraAccounts })),
);

const Billing = lazyWithRetry(() =>
import(/* webpackChunkName: "Billing" */ './billing/Billing').then(
({ Billing }) => ({ default: Billing }),
Expand Down Expand Up @@ -576,6 +582,11 @@ const MMARouter = () => {
element={<AccountOverview isFromApp />}
/>

<Route
path="/extra-accounts"
element={<ExtraAccounts />}
/>

<Route path="/billing" element={<Billing />} />
{Object.values(PRODUCT_TYPES).map(
(productType: ProductType) => (
Expand Down
5 changes: 5 additions & 0 deletions client/components/mma/MMAPageSkeleton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,11 @@ const MMALocationObjectArr: LocationObject[] = [
path: '/',
selectedNavItem: NAV_LINKS.accountOverview,
},
{
title: 'Extra accounts',
path: '/extra-accounts',
selectedNavItem: NAV_LINKS.extraAccounts,
},
{
title: 'Billing',
path: '/billing',
Expand Down
7 changes: 7 additions & 0 deletions client/components/mma/accountoverview/AccountOverview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
GROUPED_PRODUCT_TYPES,
PRODUCT_TYPES,
} from '../../../../shared/productTypes';
import { isExtraAccountsFlagEnabled } from '../../../utilities/extraAccounts';
import { useAccountDataLoader } from '../../../utilities/hooks/useAccountDataLoader';
import { useUpgradeProduct } from '../../../utilities/hooks/useUpgradePreview';
import { GenericErrorScreen } from '../../shared/GenericErrorScreen';
Expand All @@ -47,6 +48,7 @@ import { DigitalPlusUpgradeBanner } from '../shared/DigitalPlusUpgradeBanner';
import { DownloadAppCtaVariation1 } from '../shared/DownloadAppCtaVariation1';
import { DownloadEditionsAppCtaWithImage } from '../shared/DownloadEditionsAppCtaWithImage';
import { DownloadFeastAppCtaWithImage } from '../shared/DownloadFeastAppCtaWithImage';
import { ExtraAccountsBanner } from '../shared/ExtraAccountsBanner';
import type { IsFromAppProps } from '../shared/IsFromAppProps';
import { NewspaperArchiveCta } from '../shared/NewspaperArchiveCta';
import { nonServiceableCountries } from '../shared/NonServiceableCountries';
Expand Down Expand Up @@ -120,6 +122,11 @@ export const BenefitsCtas = ({ email, productKeys }: BenefitsCtasProps) => {

return (
<>
{/* TODO: remove the isExtraAccountsFlagEnabled() query-param check
once the Extra accounts feature ships; gate on Digital plus only. */}
{hasDigitalPack && isExtraAccountsFlagEnabled() && (
<ExtraAccountsBanner />
)}
{(hasDigitalPlusPrint ||
isPlusDigitalProduct ||
hasGuardianEmail ||
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import type { Decorator, Meta, StoryObj } from '@storybook/react';
import { userEvent, within } from '@storybook/test';
import { fn } from '@storybook/test';
import type { ExtraAccount } from '../../../stores/ExtraAccountsStore';
import { ToastContainer } from '../../shared/ToastContainer';
import { ExtraAccountCancelInvitationModal } from './ExtraAccountCancelInvitationModal';

const ToastDecorator: Decorator = (Story) => (
<>
<Story />
<ToastContainer />
</>
);

const pendingAccount: ExtraAccount = {
status: 'pending',
email: 'pending@example.com',
invitationCode: 'INV-001',
};

const activeAccount: ExtraAccount = {
status: 'active',
email: 'active@example.com',
name: 'Alex Active',
secondaryIdentityId: 'secondary-active',
};

const mockCancelInvitation = fn(async () => true);
const mockRemoveAccess = fn(async () => true);

export default {
title: 'Components/ExtraAccountCancelInvitationModal',
component: ExtraAccountCancelInvitationModal,
decorators: [ToastDecorator],
args: {
cancelInvitation: mockCancelInvitation,
removeAccess: mockRemoveAccess,
isSubmitting: false,
remainingInvitations: 1,
},
} as Meta<typeof ExtraAccountCancelInvitationModal>;

export const CancelInvitationClosed: StoryObj<
typeof ExtraAccountCancelInvitationModal
> = {
args: {
account: pendingAccount,
},
};

export const CancelInvitationOpen: StoryObj<
typeof ExtraAccountCancelInvitationModal
> = {
args: {
account: pendingAccount,
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
await userEvent.click(canvas.getByText('Cancel invitation'));
},
};

export const RemoveAccessClosed: StoryObj<
typeof ExtraAccountCancelInvitationModal
> = {
args: {
account: activeAccount,
},
};

export const RemoveAccessOpen: StoryObj<
typeof ExtraAccountCancelInvitationModal
> = {
args: {
account: activeAccount,
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
await userEvent.click(canvas.getByText('Remove access'));
},
};
Loading
Loading