mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-20 22:42:53 -05:00
Added Breadcrumbs toolbar option to PreviewModal in AdminX (#19016)
no issues - currently there are urls and tabs as a toolbar option for the preview modal - as part of adding offers to AdminX, we needed a breadcrumbs option for navigating between list and create/edit screens - previewToolbarBreadcrumbs is used for passing an array with the type BreadcrumbItem - onBreadcrumbsBack is used for passing a function to be called when the back button of the breadcrumbs is clicked
This commit is contained in:
parent
17f8844134
commit
8377bd8410
5 changed files with 72 additions and 3 deletions
|
@ -96,3 +96,11 @@ export const FullBleed: Story = {
|
||||||
size: 'bleed'
|
size: 'bleed'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const BreadcrumbsToolbar: Story = {
|
||||||
|
args: {
|
||||||
|
...Default.args,
|
||||||
|
previewToolbarTabs: undefined,
|
||||||
|
previewToolbarBreadcrumbs: [{label: 'Previous', onClick: () => {}}, {label: 'Current'}]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -4,6 +4,7 @@ import React, {useEffect, useState} from 'react';
|
||||||
import useGlobalDirtyState from '../../hooks/useGlobalDirtyState';
|
import useGlobalDirtyState from '../../hooks/useGlobalDirtyState';
|
||||||
import {confirmIfDirty} from '../../utils/modals';
|
import {confirmIfDirty} from '../../utils/modals';
|
||||||
import {ButtonColor, ButtonProps} from '../Button';
|
import {ButtonColor, ButtonProps} from '../Button';
|
||||||
|
import Breadcrumbs, {BreadcrumbItem} from '../Breadcrumbs';
|
||||||
import ButtonGroup from '../ButtonGroup';
|
import ButtonGroup from '../ButtonGroup';
|
||||||
import Heading, {HeadingLevel} from '../Heading';
|
import Heading, {HeadingLevel} from '../Heading';
|
||||||
import Icon from '../Icon';
|
import Icon from '../Icon';
|
||||||
|
@ -33,6 +34,7 @@ export interface PreviewModalProps {
|
||||||
deviceSelector?: boolean;
|
deviceSelector?: boolean;
|
||||||
siteLink?: string;
|
siteLink?: string;
|
||||||
previewToolbarURLs?: SelectOption[];
|
previewToolbarURLs?: SelectOption[];
|
||||||
|
previewToolbarBreadcrumbs?: BreadcrumbItem[];
|
||||||
previewBgColor?: 'grey' | 'white' | 'greygradient';
|
previewBgColor?: 'grey' | 'white' | 'greygradient';
|
||||||
selectedURL?: string;
|
selectedURL?: string;
|
||||||
previewToolbarTabs?: Tab[];
|
previewToolbarTabs?: Tab[];
|
||||||
|
@ -49,6 +51,7 @@ export interface PreviewModalProps {
|
||||||
onSelectURL?: (url: string) => void;
|
onSelectURL?: (url: string) => void;
|
||||||
onSelectDesktopView?: () => void;
|
onSelectDesktopView?: () => void;
|
||||||
onSelectMobileView?: () => void;
|
onSelectMobileView?: () => void;
|
||||||
|
onBreadcrumbsBack?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PreviewModalContent: React.FC<PreviewModalProps> = ({
|
export const PreviewModalContent: React.FC<PreviewModalProps> = ({
|
||||||
|
@ -73,6 +76,7 @@ export const PreviewModalContent: React.FC<PreviewModalProps> = ({
|
||||||
previewBgColor = 'grey',
|
previewBgColor = 'grey',
|
||||||
selectedURL,
|
selectedURL,
|
||||||
previewToolbarTabs,
|
previewToolbarTabs,
|
||||||
|
previewToolbarBreadcrumbs,
|
||||||
buttonsDisabled,
|
buttonsDisabled,
|
||||||
sidebarButtons,
|
sidebarButtons,
|
||||||
sidebarHeader,
|
sidebarHeader,
|
||||||
|
@ -85,7 +89,8 @@ export const PreviewModalContent: React.FC<PreviewModalProps> = ({
|
||||||
afterClose,
|
afterClose,
|
||||||
onSelectURL,
|
onSelectURL,
|
||||||
onSelectDesktopView,
|
onSelectDesktopView,
|
||||||
onSelectMobileView
|
onSelectMobileView,
|
||||||
|
onBreadcrumbsBack
|
||||||
}) => {
|
}) => {
|
||||||
const modal = useModal();
|
const modal = useModal();
|
||||||
const {setGlobalDirtyState} = useGlobalDirtyState();
|
const {setGlobalDirtyState} = useGlobalDirtyState();
|
||||||
|
@ -145,6 +150,16 @@ export const PreviewModalContent: React.FC<PreviewModalProps> = ({
|
||||||
width='wide'
|
width='wide'
|
||||||
onTabChange={onSelectURL!}
|
onTabChange={onSelectURL!}
|
||||||
/>;
|
/>;
|
||||||
|
} else if (previewToolbarBreadcrumbs) {
|
||||||
|
toolbarLeft = <Breadcrumbs
|
||||||
|
activeItemClassName='hidden md:!block md:!visible'
|
||||||
|
containerClassName='whitespace-nowrap'
|
||||||
|
itemClassName='hidden md:!block md:!visible'
|
||||||
|
items={previewToolbarBreadcrumbs}
|
||||||
|
separatorClassName='hidden md:!block md:!visible'
|
||||||
|
backIcon
|
||||||
|
onBack={onBreadcrumbsBack}
|
||||||
|
/>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedIconColorClass = 'text-black dark:text-green';
|
const selectedIconColorClass = 'text-black dark:text-green';
|
||||||
|
|
|
@ -433,9 +433,15 @@ const AddOfferModal = () => {
|
||||||
okColor={okProps.color}
|
okColor={okProps.color}
|
||||||
okLabel='Publish'
|
okLabel='Publish'
|
||||||
preview={iframe}
|
preview={iframe}
|
||||||
|
previewToolbarBreadcrumbs={[{label: 'Offers', onClick: () => {
|
||||||
|
updateRoute('offers/edit');
|
||||||
|
}}, {label: 'New offer'}]}
|
||||||
sidebar={sidebar}
|
sidebar={sidebar}
|
||||||
size='lg'
|
size='lg'
|
||||||
title='Offer'
|
title='Offer'
|
||||||
|
onBreadcrumbsBack={() => {
|
||||||
|
updateRoute('offers/edit');
|
||||||
|
}}
|
||||||
onCancel={cancelAddOffer}
|
onCancel={cancelAddOffer}
|
||||||
onOk={async () => {
|
onOk={async () => {
|
||||||
if (!(await handleSave({fakeWhenUnchanged: true}))) {
|
if (!(await handleSave({fakeWhenUnchanged: true}))) {
|
||||||
|
@ -444,7 +450,8 @@ const AddOfferModal = () => {
|
||||||
message: 'Can\'t save offer, please double check that you\'ve filled all mandatory fields.'
|
message: 'Can\'t save offer, please double check that you\'ve filled all mandatory fields.'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}} />;
|
}}
|
||||||
|
/>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AddOfferModal;
|
export default AddOfferModal;
|
||||||
|
|
|
@ -269,10 +269,16 @@ const EditOfferModal: React.FC<{id: string}> = ({id}) => {
|
||||||
okColor={okProps.color}
|
okColor={okProps.color}
|
||||||
okLabel={okProps.label || 'Save'}
|
okLabel={okProps.label || 'Save'}
|
||||||
preview={iframe}
|
preview={iframe}
|
||||||
|
previewToolbarBreadcrumbs={[{label: 'Offers', onClick: () => {
|
||||||
|
updateRoute('offers/edit');
|
||||||
|
}}, {label: offerById[0]?.name || ''}]}
|
||||||
sidebar={sidebar}
|
sidebar={sidebar}
|
||||||
size='lg'
|
size='lg'
|
||||||
testId='offer-update-modal'
|
testId='offer-update-modal'
|
||||||
title='Offer'
|
title='Offer'
|
||||||
|
onBreadcrumbsBack={() => {
|
||||||
|
updateRoute('offers/edit');
|
||||||
|
}}
|
||||||
onCancel={() => {
|
onCancel={() => {
|
||||||
updateRoute('offers/edit');
|
updateRoute('offers/edit');
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
|
import {Breadcrumbs} from '@tryghost/admin-x-design-system';
|
||||||
import {Button} from '@tryghost/admin-x-design-system';
|
import {Button} from '@tryghost/admin-x-design-system';
|
||||||
import {Icon} from '@tryghost/admin-x-design-system';
|
import {Icon} from '@tryghost/admin-x-design-system';
|
||||||
import {Modal} from '@tryghost/admin-x-design-system';
|
import {Modal} from '@tryghost/admin-x-design-system';
|
||||||
import {Offer, useBrowseOffersById} from '@tryghost/admin-x-framework/api/offers';
|
import {Offer, useBrowseOffersById} from '@tryghost/admin-x-framework/api/offers';
|
||||||
|
import {currencyToDecimal} from '../../../../utils/currency';
|
||||||
import {getHomepageUrl} from '@tryghost/admin-x-framework/api/site';
|
import {getHomepageUrl} from '@tryghost/admin-x-framework/api/site';
|
||||||
|
import {numberWithCommas} from '../../../../utils/helpers';
|
||||||
import {useEffect, useState} from 'react';
|
import {useEffect, useState} from 'react';
|
||||||
import {useGlobalData} from '../../../providers/GlobalDataProvider';
|
import {useGlobalData} from '../../../providers/GlobalDataProvider';
|
||||||
import {useRouting} from '@tryghost/admin-x-framework/routing';
|
import {useRouting} from '@tryghost/admin-x-framework/routing';
|
||||||
|
@ -39,7 +42,22 @@ const OfferSuccess: React.FC<{id: string}> = ({id}) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTwitter = () => {
|
const handleTwitter = () => {
|
||||||
window.open(`https://twitter.com/intent/tweet?url=${encodeURI(offerLink)}&text=${encodeURIComponent(offer?.name || '')}`, '_blank');
|
let tweetText = '';
|
||||||
|
|
||||||
|
switch (offer?.type) {
|
||||||
|
case 'percent':
|
||||||
|
tweetText = offer?.amount + '% discount';
|
||||||
|
break;
|
||||||
|
case 'fixed':
|
||||||
|
tweetText = numberWithCommas(currencyToDecimal(offer?.amount)) + ' ' + offer?.currency + ' discount';
|
||||||
|
break;
|
||||||
|
case 'trial':
|
||||||
|
tweetText = offer?.amount + ' days free trial';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
window.open(`https://twitter.com/intent/tweet?url=${encodeURI(offerLink)}&text=${encodeURIComponent(offer?.name || '')} — Check out ${encodeURIComponent(tweetText)} on:`, '_blank');
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFacebook = () => {
|
const handleFacebook = () => {
|
||||||
|
@ -61,6 +79,21 @@ const OfferSuccess: React.FC<{id: string}> = ({id}) => {
|
||||||
topRightContent='close'
|
topRightContent='close'
|
||||||
>
|
>
|
||||||
<div className='-mt-6 flex h-full flex-col items-center justify-center text-center'>
|
<div className='-mt-6 flex h-full flex-col items-center justify-center text-center'>
|
||||||
|
<div className='absolute left-6 top-5'>
|
||||||
|
<Breadcrumbs
|
||||||
|
activeItemClassName='hidden md:!block md:!visible'
|
||||||
|
containerClassName='whitespace-nowrap'
|
||||||
|
itemClassName='hidden md:!block md:!visible'
|
||||||
|
items={[{label: 'Offers', onClick: () => {
|
||||||
|
updateRoute('offers/edit');
|
||||||
|
}}, {label: offer?.name || ''}]}
|
||||||
|
separatorClassName='hidden md:!block md:!visible'
|
||||||
|
backIcon
|
||||||
|
onBack={() => {
|
||||||
|
updateRoute('offers/edit');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<Icon name='tags-check' size='xl' />
|
<Icon name='tags-check' size='xl' />
|
||||||
<h1 className='mt-6 text-4xl'>Your new offer is live!</h1>
|
<h1 className='mt-6 text-4xl'>Your new offer is live!</h1>
|
||||||
<p className='mt-3 max-w-[510px] text-[1.6rem]'>You can share the link anywhere. In your newsletter, social media, a podcast, or in-person. It all just works.</p>
|
<p className='mt-3 max-w-[510px] text-[1.6rem]'>You can share the link anywhere. In your newsletter, social media, a podcast, or in-person. It all just works.</p>
|
||||||
|
|
Loading…
Add table
Reference in a new issue