From 806fce191d1de67ebf8cffa91f083408c1c65394 Mon Sep 17 00:00:00 2001 From: Sanne de Vries <65487235+sanne-san@users.noreply.github.com> Date: Wed, 24 Jul 2024 10:26:29 +0200 Subject: [PATCH] Updated Tips & Donations settings design (#20649) REF MOM-315 - Changed to column layout - Fixed broken currency dropdown - Included a link to Stripe terms & conditions - Renamed from "Tips or donations" to "Tips & donations" --- .../src/components/Sidebar.tsx | 2 +- .../settings/growth/GrowthSettings.tsx | 6 +- .../settings/growth/TipsAndDonations.tsx | 154 ++++++++++++++++++ .../settings/growth/TipsOrDonations.tsx | 136 ---------------- .../test/e2e-browser/portal/donations.spec.js | 2 +- 5 files changed, 159 insertions(+), 141 deletions(-) create mode 100644 apps/admin-x-settings/src/components/settings/growth/TipsAndDonations.tsx delete mode 100644 apps/admin-x-settings/src/components/settings/growth/TipsOrDonations.tsx diff --git a/apps/admin-x-settings/src/components/Sidebar.tsx b/apps/admin-x-settings/src/components/Sidebar.tsx index 08f14de477..46d18a238a 100644 --- a/apps/admin-x-settings/src/components/Sidebar.tsx +++ b/apps/admin-x-settings/src/components/Sidebar.tsx @@ -186,7 +186,7 @@ const Sidebar: React.FC = () => { {hasStripeEnabled && } - {hasTipsAndDonations && } + {hasTipsAndDonations && } diff --git a/apps/admin-x-settings/src/components/settings/growth/GrowthSettings.tsx b/apps/admin-x-settings/src/components/settings/growth/GrowthSettings.tsx index 5d5f3d625f..4d74d27237 100644 --- a/apps/admin-x-settings/src/components/settings/growth/GrowthSettings.tsx +++ b/apps/admin-x-settings/src/components/settings/growth/GrowthSettings.tsx @@ -3,13 +3,13 @@ import Offers from './Offers'; import React from 'react'; import Recommendations from './Recommendations'; import SearchableSection from '../../SearchableSection'; -import TipsOrDonations from './TipsOrDonations'; +import TipsAndDonations from './TipsAndDonations'; import useFeatureFlag from '../../../hooks/useFeatureFlag'; import {checkStripeEnabled} from '@tryghost/admin-x-framework/api/settings'; import {useGlobalData} from '../../providers/GlobalDataProvider'; export const searchKeywords = { - tips: ['growth', 'tip', 'donation', 'one time', 'payment'], + tips: ['growth', 'tips', 'donations', 'one time', 'payment'], embedSignupForm: ['growth', 'embeddable signup form', 'embeddable form', 'embeddable sign up form', 'embeddable sign up'], recommendations: ['growth', 'recommendations', 'recommend', 'blogroll'], offers: ['growth', 'offers', 'discounts', 'coupons', 'promotions'] @@ -25,7 +25,7 @@ const GrowthSettings: React.FC = () => { {hasStripeEnabled && } - {hasTipsAndDonations && } + {hasTipsAndDonations && } ); }; diff --git a/apps/admin-x-settings/src/components/settings/growth/TipsAndDonations.tsx b/apps/admin-x-settings/src/components/settings/growth/TipsAndDonations.tsx new file mode 100644 index 0000000000..10cbf0ed67 --- /dev/null +++ b/apps/admin-x-settings/src/components/settings/growth/TipsAndDonations.tsx @@ -0,0 +1,154 @@ +import React, {useEffect, useState} from 'react'; +import TopLevelGroup from '../../TopLevelGroup'; +import useSettingGroup from '../../../hooks/useSettingGroup'; +import {Button, CurrencyField, Heading, Select, SettingGroupContent, confirmIfDirty, withErrorBoundary} from '@tryghost/admin-x-design-system'; +import {currencySelectGroups, getSymbol, validateCurrencyAmount} from '../../../utils/currency'; +import {getSettingValues} from '@tryghost/admin-x-framework/api/settings'; + +// Stripe doesn't allow amounts over 10,000 as a preset amount +const MAX_AMOUNT = 10_000; + +const TipsAndDonations: React.FC<{ keywords: string[] }> = ({keywords}) => { + const { + localSettings, + siteData, + updateSetting, + isEditing, + saveState, + handleSave, + handleCancel, + focusRef, + handleEditingChange, + errors, + validate, + clearError + } = useSettingGroup({ + onValidate: () => { + return { + donationsSuggestedAmount: validateCurrencyAmount(suggestedAmountInCents, donationsCurrency, {maxAmount: MAX_AMOUNT}) + }; + } + }); + + const [donationsCurrency = 'USD', donationsSuggestedAmount = '0'] = getSettingValues( + localSettings, + ['donations_currency', 'donations_suggested_amount'] + ); + + const suggestedAmountInCents = parseInt(donationsSuggestedAmount); + const suggestedAmountInDollars = suggestedAmountInCents / 100; + const donateUrl = `${siteData?.url.replace(/\/$/, '')}/#/portal/support`; + + useEffect(() => { + validate(); + }, [donationsCurrency]); // eslint-disable-line react-hooks/exhaustive-deps + + const [copied, setCopied] = useState(false); + + const copyDonateUrl = () => { + navigator.clipboard.writeText(donateUrl); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }; + + const openPreview = () => { + confirmIfDirty(saveState === 'unsaved', () => window.open(donateUrl, '_blank')); + }; + + const values = ( + +
+ Shareable link +
+
+ {donateUrl} +
+
+
+ + ) + } + ]} + /> + ); + + const inputFields = ( + +
+ group.options).find(option => option.value === donationsCurrency)} + title='Currency' + hideTitle + isSearchable + onSelect={option => updateSetting('donations_currency', option?.value || 'USD')} + /> + )} + title='Suggested amount' + valueInCents={parseInt(donationsSuggestedAmount)} + onBlur={validate} + onChange={cents => updateSetting('donations_suggested_amount', cents.toString())} + onKeyDown={() => clearError('donationsSuggestedAmount')} + /> +
+
+
+ Shareable link +
+
+ {donateUrl} +
+
+
+
+
+ ); + + return ( + + {isEditing ? inputFields : values} +
+ All tips and donations are subject to Stripe's tipping policy. +
+
+ ); +}; + +export default withErrorBoundary(TipsAndDonations, 'Tips & donations'); diff --git a/apps/admin-x-settings/src/components/settings/growth/TipsOrDonations.tsx b/apps/admin-x-settings/src/components/settings/growth/TipsOrDonations.tsx deleted file mode 100644 index 64726daddb..0000000000 --- a/apps/admin-x-settings/src/components/settings/growth/TipsOrDonations.tsx +++ /dev/null @@ -1,136 +0,0 @@ -import React, {useEffect, useState} from 'react'; -import TopLevelGroup from '../../TopLevelGroup'; -import useSettingGroup from '../../../hooks/useSettingGroup'; -import {Button, CurrencyField, Heading, Select, SettingGroupContent, confirmIfDirty, withErrorBoundary} from '@tryghost/admin-x-design-system'; -import {currencySelectGroups, getSymbol, validateCurrencyAmount} from '../../../utils/currency'; -import {getSettingValues} from '@tryghost/admin-x-framework/api/settings'; - -// Stripe doesn't allow amounts over 10,000 as a preset amount -const MAX_AMOUNT = 10_000; - -const TipsOrDonations: React.FC<{ keywords: string[] }> = ({keywords}) => { - const { - localSettings, - siteData, - updateSetting, - isEditing, - saveState, - handleSave, - handleCancel, - focusRef, - handleEditingChange, - errors, - validate, - clearError - } = useSettingGroup({ - onValidate: () => { - return { - donationsSuggestedAmount: validateCurrencyAmount(suggestedAmountInCents, donationsCurrency, {maxAmount: MAX_AMOUNT}) - }; - } - }); - - const [donationsCurrency = 'USD', donationsSuggestedAmount = '0'] = getSettingValues( - localSettings, - ['donations_currency', 'donations_suggested_amount'] - ); - - const suggestedAmountInCents = parseInt(donationsSuggestedAmount); - const suggestedAmountInDollars = suggestedAmountInCents / 100; - const donateUrl = `${siteData?.url.replace(/\/$/, '')}/#/portal/support`; - - useEffect(() => { - validate(); - }, [donationsCurrency]); // eslint-disable-line react-hooks/exhaustive-deps - - const [copied, setCopied] = useState(false); - - const copyDonateUrl = () => { - navigator.clipboard.writeText(donateUrl); - setCopied(true); - setTimeout(() => setCopied(false), 2000); - }; - - const openPreview = () => { - confirmIfDirty(saveState === 'unsaved', () => window.open(donateUrl, '_blank')); - }; - - const values = ( - -
- Shareable link — - -
-
- {donateUrl} -
-
-
- - ) - } - ]} - /> - ); - - const inputFields = ( - - group.options).find(option => option.value === donationsCurrency)} - title='Currency' - hideTitle - isSearchable - onSelect={option => updateSetting('donations_currency', option?.value || 'USD')} - /> - )} - title='Suggested amount' - valueInCents={parseInt(donationsSuggestedAmount)} - onBlur={validate} - onChange={cents => updateSetting('donations_suggested_amount', cents.toString())} - onKeyDown={() => clearError('donationsSuggestedAmount')} - /> - - ); - - return ( - - {isEditing ? inputFields : values} - - ); -}; - -export default withErrorBoundary(TipsOrDonations, 'Tips or donations'); diff --git a/ghost/core/test/e2e-browser/portal/donations.spec.js b/ghost/core/test/e2e-browser/portal/donations.spec.js index e772e65367..93f170ea02 100644 --- a/ghost/core/test/e2e-browser/portal/donations.spec.js +++ b/ghost/core/test/e2e-browser/portal/donations.spec.js @@ -46,7 +46,7 @@ test.describe('Portal', () => { test('Can donate with a fixed amount set and different currency', async ({sharedPage}) => { await sharedPage.goto('/ghost/#/settings'); - const section = sharedPage.getByTestId('tips-or-donations'); + const section = sharedPage.getByTestId('tips-and-donations'); await section.getByRole('button', {name: 'Edit'}).click(); await section.getByLabel('Suggested amount').fill('98');