diff --git a/apps/admin-x-design-system/src/global/TabView.stories.tsx b/apps/admin-x-design-system/src/global/TabView.stories.tsx index 62ab492694..40063bb6be 100644 --- a/apps/admin-x-design-system/src/global/TabView.stories.tsx +++ b/apps/admin-x-design-system/src/global/TabView.stories.tsx @@ -54,3 +54,10 @@ export const WithCounter: Story = { tabs: tabsWithCounters } }; + +export const WithTopRightContent: Story = { + args: { + tabs: tabs, + topRightContent:

Some content

+ } +}; diff --git a/apps/admin-x-design-system/src/global/TabView.tsx b/apps/admin-x-design-system/src/global/TabView.tsx index 9351873e5f..3d58dce7cf 100644 --- a/apps/admin-x-design-system/src/global/TabView.tsx +++ b/apps/admin-x-design-system/src/global/TabView.tsx @@ -61,7 +61,8 @@ export interface TabListProps { handleTabChange?: (e: React.MouseEvent) => void; border: boolean; buttonBorder?: boolean; - selectedTab?: ID + selectedTab?: ID, + topRightContent?: React.ReactNode } export const TabList: React.FC = ({ @@ -70,7 +71,8 @@ export const TabList: React.FC = ({ handleTabChange, border, buttonBorder, - selectedTab + selectedTab, + topRightContent }) => { const containerClasses = clsx( 'no-scrollbar flex w-full overflow-x-auto', @@ -93,6 +95,10 @@ export const TabList: React.FC = ({ /> ))} + {topRightContent !== null ? +
{topRightContent}
: + null + } ); }; @@ -105,6 +111,7 @@ export interface TabViewProps { buttonBorder?: boolean; width?: TabWidth; containerClassName?: string; + topRightContent?: React.ReactNode; testId?: string; } @@ -116,7 +123,8 @@ function TabView({ border = true, buttonBorder = border, width = 'normal', - containerClassName + containerClassName, + topRightContent }: TabViewProps) { if (tabs.length !== 0 && selectedTab === undefined) { selectedTab = tabs[0].id; @@ -139,6 +147,7 @@ function TabView({ handleTabChange={handleTabChange} selectedTab={selectedTab} tabs={tabs} + topRightContent={topRightContent} width={width} /> {tabs.map((tab) => { diff --git a/apps/admin-x-settings/src/components/settings/growth/Offers.tsx b/apps/admin-x-settings/src/components/settings/growth/Offers.tsx index 9b504f7934..2c123cda4f 100644 --- a/apps/admin-x-settings/src/components/settings/growth/Offers.tsx +++ b/apps/admin-x-settings/src/components/settings/growth/Offers.tsx @@ -77,14 +77,14 @@ const Offers: React.FC<{ keywords: string[] }> = ({keywords}) => { offerButtonLink = openTiers; descriptionButtonText = ''; } else if (paidActiveTiers.length > 0 && allOffers.length === 0) { - offerButtonText = 'Add offers'; + offerButtonText = 'Add offer'; offerButtonLink = openAddModal; } return ( } - description={<>Create discounts & coupons to boost new subscriptions. {allOffers.length === 0 && <>
{descriptionButtonText}}} + description={<>Create discounts & coupons to boost new subscriptions. {allOffers.length === 0 && <>{descriptionButtonText}}} keywords={keywords} navid='offers' testId='offers' @@ -115,12 +115,9 @@ const Offers: React.FC<{ keywords: string[] }> = ({keywords}) => { } {paidActiveTiers.length === 0 && allOffers.length === 0 ? (
-
- <>You must have an active tier to create an offer. -
-
-
+ You must have an active tier to create an offer. + {` `} +
) : '' } diff --git a/apps/admin-x-settings/src/components/settings/growth/offers/OffersIndex.tsx b/apps/admin-x-settings/src/components/settings/growth/offers/OffersIndex.tsx index 0b8d20cc4c..5328706e42 100644 --- a/apps/admin-x-settings/src/components/settings/growth/offers/OffersIndex.tsx +++ b/apps/admin-x-settings/src/components/settings/growth/offers/OffersIndex.tsx @@ -1,7 +1,7 @@ import {Button, Tab, TabView} from '@tryghost/admin-x-design-system'; import {ButtonGroup, ButtonProps, showToast} from '@tryghost/admin-x-design-system'; +import {Icon} from '@tryghost/admin-x-design-system'; import {Modal} from '@tryghost/admin-x-design-system'; -import {NoValueLabel} from '@tryghost/admin-x-design-system'; import {SortMenu} from '@tryghost/admin-x-design-system'; import {Tier, getPaidActiveTiers, useBrowseTiers} from '@tryghost/admin-x-framework/api/tiers'; import {Tooltip} from '@tryghost/admin-x-design-system'; @@ -90,6 +90,15 @@ export const CopyLinkButton: React.FC<{offerCode: string}> = ({offerCode}) => { return + +); + export const OffersIndexModal = () => { const modal = useModal(); const {updateRoute} = useRouting(); @@ -221,66 +230,68 @@ export const OffersIndexModal = () => { backDropClick={false} cancelLabel='' footer={false} - header={false} height='full' size='lg' testId='offers-modal' + title='Offers' + topRightContent={} width={1140} > -
+
-
-
- -
- -
-
-

{offersTabs.find(tab => tab.id === selectedTab)?.title} offers

-
- { - const newDirection = selectedDirection === 'asc' ? 'desc' : 'asc'; - setSortingState?.([{ - type: 'offers', - option: sortOption, - direction: newDirection - }]); - }} - onSortChange={(selectedOption) => { - setSortingState?.([{ - type: 'offers', - option: selectedOption, - direction: sortDirection - }]); - }} - /> -
-
+ 0) || (selectedTab === 'archived' && archivedOffers.length > 0) ? +
+ { + const newDirection = selectedDirection === 'asc' ? 'desc' : 'asc'; + setSortingState?.([{ + type: 'offers', + option: sortOption, + direction: newDirection + }]); + }} + onSortChange={(selectedOption) => { + setSortingState?.([{ + type: 'offers', + option: selectedOption, + direction: sortDirection + }]); + }} + /> +
: + null + } + onTabChange={setSelectedTab} + />
{selectedTab === 'active' && activeOffers.length === 0 && !isFetchingOffers ? - - No offers found. - : + updateRoute('offers/new')} + buttonLabel='Create an offer' + description='Grow your audience with discounts or free trials.' + /> : null } {selectedTab === 'archived' && archivedOffers.length === 0 && !isFetchingOffers ? - - No offers found. - : + setSelectedTab('active')} + buttonLabel='Back to active' + description='All archived offers will be shown here.' + /> : null } {listLayoutOutput} diff --git a/apps/admin-x-settings/test/acceptance/membership/offers.test.ts b/apps/admin-x-settings/test/acceptance/membership/offers.test.ts index 00e32bb66a..4bb4db4b03 100644 --- a/apps/admin-x-settings/test/acceptance/membership/offers.test.ts +++ b/apps/admin-x-settings/test/acceptance/membership/offers.test.ts @@ -156,7 +156,7 @@ test.describe('Offers Modal', () => { const section = page.getByTestId('offers'); await section.getByRole('button', {name: 'Manage offers'}).click(); const modal = page.getByTestId('offers-modal'); - await expect(modal).toContainText('Active offers'); + await expect(modal.getByText('Active')).toHaveAttribute('aria-selected', 'true'); await expect(modal).toContainText('First offer'); await expect(modal).toContainText('Second offer'); }); @@ -175,7 +175,7 @@ test.describe('Offers Modal', () => { await section.getByRole('button', {name: 'Manage offers'}).click(); const modal = page.getByTestId('offers-modal'); await modal.getByText('Archived').click(); - await expect(modal).toContainText('Archived offers'); + await expect(modal.getByText('Archived')).toHaveAttribute('aria-selected', 'true'); await expect(modal).toContainText('Third offer'); }); @@ -200,7 +200,7 @@ test.describe('Offers Modal', () => { const section = page.getByTestId('offers'); await section.getByRole('button', {name: 'Manage offers'}).click(); const modal = page.getByTestId('offers-modal'); - await expect(modal).toContainText('Active offers'); + await expect(modal.getByText('Active')).toHaveAttribute('aria-selected', 'true'); await expect(modal).toContainText('First offer'); await modal.getByText('First offer').click();