From 704b04120222a8d71cd8137b49f1c0d38b766a35 Mon Sep 17 00:00:00 2001 From: Jono M Date: Wed, 11 Oct 2023 15:21:16 +0100 Subject: [PATCH] Fixed handling of null URLs in tier add form (#18575) refs https://github.com/TryGhost/Product/issues/3832 Updates to match the old admin, where nothing in the text field means null and the URL is not formatted until you enter something. --- .../admin-x-ds/global/form/URLTextField.tsx | 28 ++++++++++++------- .../membership/tiers/TierDetailModal.tsx | 6 ++-- .../site/navigation/NavigationItemEditor.tsx | 2 +- .../AddRecommendationModal.tsx | 4 +-- .../RecommendationDescriptionForm.tsx | 2 +- 5 files changed, 26 insertions(+), 16 deletions(-) diff --git a/apps/admin-x-settings/src/admin-x-ds/global/form/URLTextField.tsx b/apps/admin-x-settings/src/admin-x-ds/global/form/URLTextField.tsx index 51be570bad..d25c509e8c 100644 --- a/apps/admin-x-settings/src/admin-x-ds/global/form/URLTextField.tsx +++ b/apps/admin-x-settings/src/admin-x-ds/global/form/URLTextField.tsx @@ -3,7 +3,11 @@ import TextField, {TextFieldProps} from './TextField'; import validator from 'validator'; import {useFocusContext} from '../../providers/DesignSystemProvider'; -export const formatUrl = (value: string, baseUrl?: string) => { +export const formatUrl = (value: string, baseUrl?: string, nullable?: boolean) => { + if (nullable && !value) { + return {save: null, display: ''}; + } + let url = value.trim(); if (!url) { @@ -98,25 +102,27 @@ export const formatUrl = (value: string, baseUrl?: string) => { * - Anchor links are displayed and saved as-is (e.g. `#test`) * - Values that don't look like URLs are displayed and saved as-is (e.g. `test`) */ -const URLTextField: React.FC & { +const URLTextField: React.FC & { baseUrl?: string; transformPathWithoutSlash?: boolean; - onChange: (value: string) => void; -}> = ({baseUrl, value, transformPathWithoutSlash, onChange, ...props}) => { + nullable?: boolean; + value: string | null; + onChange: (value: string | null) => void; +}> = ({baseUrl, value, transformPathWithoutSlash, nullable, onChange, ...props}) => { const [displayedUrl, setDisplayedUrl] = useState(''); const {setFocusState} = useFocusContext(); useEffect(() => { - setDisplayedUrl(formatUrl(value || '', baseUrl).display); - }, [value, baseUrl]); + setDisplayedUrl(formatUrl(value || '', baseUrl, nullable).display); + }, [value, baseUrl, nullable]); const updateUrl = () => { - let urls = formatUrl(displayedUrl, baseUrl); + let urls = formatUrl(displayedUrl, baseUrl, nullable); // If the user entered something like "bla", try to parse it as a relative URL // If parsing as "/bla" results in a valid URL, use that instead - if (transformPathWithoutSlash && !urls.display.includes('//')) { - const candidate = formatUrl('/' + displayedUrl, baseUrl); + if (transformPathWithoutSlash && !urls.display.includes('//') && (displayedUrl || !nullable)) { + const candidate = formatUrl('/' + displayedUrl, baseUrl, nullable); if (candidate.display.includes('//')) { urls = candidate; @@ -124,7 +130,9 @@ const URLTextField: React.FC & { } setDisplayedUrl(urls.display); - onChange(urls.save); + if (urls.save !== value) { + onChange(urls.save); + } setFocusState(false); }; diff --git a/apps/admin-x-settings/src/components/settings/membership/tiers/TierDetailModal.tsx b/apps/admin-x-settings/src/components/settings/membership/tiers/TierDetailModal.tsx index 0742cd7035..c36b8cb8fa 100644 --- a/apps/admin-x-settings/src/components/settings/membership/tiers/TierDetailModal.tsx +++ b/apps/admin-x-settings/src/components/settings/membership/tiers/TierDetailModal.tsx @@ -53,7 +53,8 @@ const TierDetailModalContent: React.FC<{tier?: Tier}> = ({tier}) => { ...(tier || {}), trial_days: tier?.trial_days?.toString() || '', currency: tier?.currency || currencies[0].isoCode, - visibility: tier?.visibility || 'none' + visibility: tier?.visibility || 'none', + welcome_page_url: tier?.welcome_page_url || null }, onSave: async () => { const {trial_days: trialDays, currency, ...rest} = formState; @@ -281,7 +282,8 @@ const TierDetailModalContent: React.FC<{tier?: Tier}> = ({tier}) => { hint='Redirect to this URL after signup for premium membership' placeholder={siteData?.url} title='Welcome page' - value={formState.welcome_page_url || ''} + value={formState.welcome_page_url || null} + nullable transformPathWithoutSlash onChange={value => updateForm(state => ({...state, welcome_page_url: value || null}))} /> diff --git a/apps/admin-x-settings/src/components/settings/site/navigation/NavigationItemEditor.tsx b/apps/admin-x-settings/src/components/settings/site/navigation/NavigationItemEditor.tsx index c02d00aec6..8ef5fc8916 100644 --- a/apps/admin-x-settings/src/components/settings/site/navigation/NavigationItemEditor.tsx +++ b/apps/admin-x-settings/src/components/settings/site/navigation/NavigationItemEditor.tsx @@ -44,7 +44,7 @@ const NavigationItemEditor: React.FC = ({baseUrl, ite unstyled={unstyled} value={item.url} hideTitle - onChange={value => updateItem?.({url: value})} + onChange={value => updateItem?.({url: value || ''})} onKeyDown={() => clearError?.('url')} /> diff --git a/apps/admin-x-settings/src/components/settings/site/recommendations/AddRecommendationModal.tsx b/apps/admin-x-settings/src/components/settings/site/recommendations/AddRecommendationModal.tsx index 2aeda99a91..96800e0a3a 100644 --- a/apps/admin-x-settings/src/components/settings/site/recommendations/AddRecommendationModal.tsx +++ b/apps/admin-x-settings/src/components/settings/site/recommendations/AddRecommendationModal.tsx @@ -22,7 +22,7 @@ interface AddRecommendationModalProps { } const doFormatUrl = (url: string) => { - return formatUrl(url).save; + return formatUrl(url).save || ''; }; const validateUrl = function (errors: ErrorMessages, url: string) { @@ -60,7 +60,7 @@ const AddRecommendationModal: React.FC updateForm((state) => { return { ...state, - url: u + url: u || '' }; })} />}