0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-03 23:00:14 -05:00

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.
This commit is contained in:
Jono M 2023-10-11 15:21:16 +01:00 committed by GitHub
parent 5bcc7f9a7a
commit 704b041202
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 26 additions and 16 deletions

View file

@ -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<Omit<TextFieldProps, 'onChange'> & {
const URLTextField: React.FC<Omit<TextFieldProps, 'value' | 'onChange'> & {
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<Omit<TextFieldProps, 'onChange'> & {
}
setDisplayedUrl(urls.display);
onChange(urls.save);
if (urls.save !== value) {
onChange(urls.save);
}
setFocusState(false);
};

View file

@ -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}))}
/>

View file

@ -44,7 +44,7 @@ const NavigationItemEditor: React.FC<NavigationItemEditorProps> = ({baseUrl, ite
unstyled={unstyled}
value={item.url}
hideTitle
onChange={value => updateItem?.({url: value})}
onChange={value => updateItem?.({url: value || ''})}
onKeyDown={() => clearError?.('url')}
/>
</div>

View file

@ -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<RoutingModalProps & AddRecommendationModa
const {formState, updateForm, handleSave, errors, saveState, clearError, setErrors} = useForm({
initialState: recommendation ?? {
title: '',
url: initialUrlCleaned,
url: initialUrlCleaned || '',
description: '',
excerpt: null,
featured_image: null,

View file

@ -96,7 +96,7 @@ const RecommendationDescriptionForm: React.FC<Props<EditOrAddRecommendation | Re
onChange={u => updateForm((state) => {
return {
...state,
url: u
url: u || ''
};
})}
/>}