Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions src/app/[lang]/settings/SettingsRouteClient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export const SettingsRouteClient = ({
pushNotifications: true,
matchAlert: true,
profileInteractionAlert: true,
profileViewAlert: false,
theme: 'dark',
applicationLanguage: locale,
timeFormat: '24hr',
Expand Down
31 changes: 31 additions & 0 deletions src/features/Settings/CompareTable.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { Meta, StoryObj } from '@storybook/react';
import { expect, within } from '@storybook/test';
import { CompareTable } from './CompareTable';

const meta: Meta<typeof CompareTable> = {
title: 'Features/Settings/CompareTable',
component: CompareTable,
decorators: [
(Story) => (
<div className="mx-auto max-w-[640px] rounded-3xl bg-background-dark p-6">
<Story />
</div>
),
],
};

export default meta;
type Story = StoryObj<typeof CompareTable>;

export const Default: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);

await expect(canvas.getByText('Feature')).toBeInTheDocument();
await expect(canvas.getByText('Bump cooldown')).toBeInTheDocument();
await expect(canvas.getByText('Profile view alerts')).toBeInTheDocument();
await expect(canvas.getByText('Every 1 h 30 m')).toBeInTheDocument();
await expect(canvas.getByText('See who viewed you')).toBeInTheDocument();
await expect(canvas.getAllByText('No')).toHaveLength(2);
},
};
104 changes: 104 additions & 0 deletions src/features/Settings/CompareTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
'use client';

import { useTranslations } from 'next-intl';

const COLUMNS =
'grid-cols-[minmax(0,1fr)_90px_130px] min-[720px]:grid-cols-[minmax(0,1fr)_150px_170px]';

type CompareRow = {
feature: string;
sub: string;
free: string | null;
premium: string;
};

const CMP_ROWS: CompareRow[] = [
{
feature: 'compareBumpFeature',
sub: 'compareBumpSub',
free: 'compareBumpFree',
premium: 'compareBumpPremium',
},
{
feature: 'compareLanguagesFeature',
sub: 'compareLanguagesSub',
free: 'compareLanguagesFree',
premium: 'compareLanguagesPremium',
},
{
feature: 'compareTagsFeature',
sub: 'compareTagsSub',
free: 'compareTagsFree',
premium: 'compareTagsPremium',
},
{
feature: 'compareColoursFeature',
sub: 'compareColoursSub',
free: 'compareColoursFree',
premium: 'compareColoursPremium',
},
{
feature: 'compareVoiceFeature',
sub: 'compareVoiceSub',
free: null,
premium: 'compareVoicePremium',
},
{
feature: 'compareCopyFeature',
sub: 'compareCopySub',
free: 'compareCopyFree',
premium: 'compareCopyPremium',
},
{
feature: 'compareViewFeature',
sub: 'compareViewSub',
free: null,
premium: 'compareViewPremium',
},
];

export const CompareTable = () => {
const t = useTranslations('Settings');

return (
<div className="overflow-hidden rounded-2xl border border-white/10">
<div className={`grid items-center ${COLUMNS}`}>
<div className="px-4 py-3 font-semibold text-[11px] text-gray-500 uppercase tracking-[0.06em]">
{t('compareFeatureHeader')}
</div>
<div className="px-4 py-3 font-semibold text-[11px] text-gray-500 uppercase tracking-[0.06em]">
{t('compareFreeHeader')}
</div>
<div className="flex h-full items-center bg-background-darker px-4 py-3 font-semibold text-[11px] text-white uppercase tracking-[0.06em]">
{t('comparePremiumHeader')}
</div>
</div>
{CMP_ROWS.map((row) => (
<div
key={row.feature}
className={`grid items-center border-white/10 border-t ${COLUMNS}`}
>
<div className="px-4 py-3.5">
<p className="font-medium text-[14px] text-white">
{t(row.feature)}
</p>
<p className="mt-0.5 text-[12px] text-gray-500">{t(row.sub)}</p>
</div>
<div className="px-4 py-3.5 font-light text-[13.5px] text-gray-400">
{row.free ? (
t(row.free)
) : (
<span className="text-gray-500">{t('compareNoValue')}</span>
)}
</div>
<div className="flex h-full items-center gap-[7px] bg-background-darker px-4 py-3.5 font-medium text-[13.5px] text-white">
<span aria-hidden="true" className="font-bold text-primary-light">
</span>
{t(row.premium)}
</div>
</div>
))}
</div>
);
};
73 changes: 73 additions & 0 deletions src/features/Settings/SettingsPage.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const defaultSettings: SettingsFormValues = {
pushNotifications: true,
matchAlert: true,
profileInteractionAlert: true,
profileViewAlert: true,
theme: 'dark',
applicationLanguage: 'en',
timeFormat: '24hr',
Expand Down Expand Up @@ -144,3 +145,75 @@ export const SaveFlow: Story = {
);
},
};

export const PremiumTabFree: Story = {
args: { premium: false },
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);

fireEvent.click(canvas.getByRole('button', { name: 'Premium' }));

await waitFor(() =>
expect(canvas.getByText('Bump cooldown')).toBeInTheDocument(),
);
expect(canvas.getByText('Every 1 h 30 m')).toBeInTheDocument();
expect(canvas.getByText('See who viewed you')).toBeInTheDocument();
expect(canvas.getAllByText('No')).toHaveLength(2);

expect(
canvas.getByText('$2.99 / month · cancel anytime'),
).toBeInTheDocument();
expect(canvas.getByRole('button', { name: 'Upgrade' })).toBeInTheDocument();
expect(
canvas.queryByRole('button', { name: 'Manage billing' }),
).not.toBeInTheDocument();
},
};

export const PremiumTabPremium: Story = {
args: { premium: true },
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);

fireEvent.click(canvas.getByRole('button', { name: 'Premium' }));

await waitFor(() =>
expect(
canvas.getByText('Your plan: everything below is included.'),
).toBeInTheDocument(),
);
expect(canvas.getByText('$2.99')).toBeInTheDocument();
expect(canvas.getByText('Bump cooldown')).toBeInTheDocument();
expect(
canvas.getByRole('button', { name: 'Manage billing' }),
).toBeInTheDocument();
expect(
canvas.queryByRole('button', { name: 'Upgrade' }),
).not.toBeInTheDocument();
},
};

export const GatedProfileViewAlert: Story = {
args: { premium: false },
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);

fireEvent.click(canvas.getByRole('button', { name: 'Notifications' }));

await waitFor(() =>
expect(canvas.getByText('Profile View Alert')).toBeInTheDocument(),
);
expect(canvas.getAllByText('Premium')).toHaveLength(2);

fireEvent.click(canvas.getByRole('switch', { name: 'Profile View Alert' }));

await waitFor(() =>
expect(canvas.getByText('Bump cooldown')).toBeInTheDocument(),
);
expect(
canvas.getByText(
'Everything in Free, plus more room to learn and be found.',
),
).toBeInTheDocument();
},
};
Loading
Loading