diff --git a/apps/admin-x-settings/src/admin-x-ds/global/Button.tsx b/apps/admin-x-settings/src/admin-x-ds/global/Button.tsx index 3ffe39e57f..f26b6c30c5 100644 --- a/apps/admin-x-settings/src/admin-x-ds/global/Button.tsx +++ b/apps/admin-x-settings/src/admin-x-ds/global/Button.tsx @@ -15,9 +15,10 @@ export interface ButtonProps { fullWidth?: boolean; link?: boolean; disabled?: boolean; + unstyled?: boolean; className?: string; tag?: string; - onClick?: () => void; + onClick?: (e?:React.MouseEvent) => void; } const Button: React.FC = ({ @@ -30,6 +31,7 @@ const Button: React.FC = ({ fullWidth, link, disabled, + unstyled = false, className = '', tag = 'button', onClick, @@ -41,33 +43,36 @@ const Button: React.FC = ({ let styles = ''; - styles += ' transition whitespace-nowrap flex items-center justify-center rounded-sm text-sm'; - styles += ((link && color !== 'clear' && color !== 'black') || (!link && color !== 'clear')) ? ' font-bold' : ' font-semibold'; - styles += !link ? `${size === 'sm' ? ' px-3 h-7 ' : ' px-4 h-[34px] '}` : ''; + if (!unstyled) { + styles += ' transition whitespace-nowrap flex items-center justify-center rounded-sm text-sm'; + styles += ((link && color !== 'clear' && color !== 'black') || (!link && color !== 'clear')) ? ' font-bold' : ' font-semibold'; + styles += !link ? `${size === 'sm' ? ' px-3 h-7 ' : ' px-4 h-[34px] '}` : ''; - switch (color) { - case 'black': - styles += link ? ' text-black hover:text-grey-800' : ' bg-black text-white hover:bg-grey-900'; - break; - case 'grey': - styles += link ? ' text-black hover:text-grey-800' : ' bg-grey-100 text-black hover:!bg-grey-300'; - break; - case 'green': - styles += link ? ' text-green hover:text-green-400' : ' bg-green text-white hover:bg-green-400'; - break; - case 'red': - styles += link ? ' text-red hover:text-red-400' : ' bg-red text-white hover:bg-red-400'; - break; - case 'white': - styles += link ? ' text-white hover:text-white' : ' bg-white text-black'; - break; - default: - styles += link ? ' text-black hover:text-grey-800' : ' text-black hover:bg-grey-200'; - break; + switch (color) { + case 'black': + styles += link ? ' text-black hover:text-grey-800' : ' bg-black text-white hover:bg-grey-900'; + break; + case 'grey': + styles += link ? ' text-black hover:text-grey-800' : ' bg-grey-100 text-black hover:!bg-grey-300'; + break; + case 'green': + styles += link ? ' text-green hover:text-green-400' : ' bg-green text-white hover:bg-green-400'; + break; + case 'red': + styles += link ? ' text-red hover:text-red-400' : ' bg-red text-white hover:bg-red-400'; + break; + case 'white': + styles += link ? ' text-white hover:text-white' : ' bg-white text-black'; + break; + default: + styles += link ? ' text-black hover:text-grey-800' : ' text-black hover:bg-grey-200'; + break; + } + + styles += (fullWidth && !link) ? ' w-full' : ''; + styles += (disabled) ? ' opacity-40' : ' cursor-pointer'; } - styles += (fullWidth && !link) ? ' w-full' : ''; - styles += (disabled) ? ' opacity-40' : ' cursor-pointer'; styles += ` ${className}`; const buttonChildren = <> diff --git a/apps/admin-x-settings/src/admin-x-ds/global/List.tsx b/apps/admin-x-settings/src/admin-x-ds/global/List.tsx index 2870bb42ab..e3a4f294cc 100644 --- a/apps/admin-x-settings/src/admin-x-ds/global/List.tsx +++ b/apps/admin-x-settings/src/admin-x-ds/global/List.tsx @@ -16,27 +16,42 @@ interface ListProps { title?: React.ReactNode; titleSeparator?: boolean; children?: React.ReactNode; + actions?: React.ReactNode; hint?: React.ReactNode; hintSeparator?: boolean; borderTop?: boolean; + className?: string; } -const List: React.FC = ({title, titleSeparator, children, hint, hintSeparator, borderTop, pageTitle}) => { +const List: React.FC = ({title, titleSeparator, children, actions, hint, hintSeparator, borderTop, pageTitle, className}) => { titleSeparator = (titleSeparator === undefined) ? true : titleSeparator; hintSeparator = (hintSeparator === undefined) ? true : hintSeparator; const listClasses = clsx( (borderTop || pageTitle) && 'border-t border-grey-300', - pageTitle && 'mt-14' + pageTitle && 'mt-14', + className ); + let heading; + + if (title) { + const headingTitle = {title}; + heading = actions ? ( +
+ {headingTitle} + {actions} +
+ ) : headingTitle; + } + return ( <> {pageTitle && {pageTitle}}
{(!pageTitle && title) && -
- {title} +
+ {heading} {titleSeparator && }
} diff --git a/apps/admin-x-settings/src/admin-x-ds/global/ListItem.tsx b/apps/admin-x-settings/src/admin-x-ds/global/ListItem.tsx index 531e037af0..6cdbd9ee5f 100644 --- a/apps/admin-x-settings/src/admin-x-ds/global/ListItem.tsx +++ b/apps/admin-x-settings/src/admin-x-ds/global/ListItem.tsx @@ -18,9 +18,10 @@ interface ListItemProps { bgOnHover?: boolean; onClick?: (e: React.MouseEvent) => void; + children?: React.ReactNode; } -const ListItem: React.FC = ({id, title, detail, action, hideActions, avatar, className, testId, separator, bgOnHover = true, onClick}) => { +const ListItem: React.FC = ({id, title, detail, action, hideActions, avatar, className, testId, separator, bgOnHover = true, onClick, children}) => { const handleClick = (e: React.MouseEvent) => { onClick?.(e); }; @@ -29,19 +30,21 @@ const ListItem: React.FC = ({id, title, detail, action, hideActio const listItemClasses = clsx( 'group flex items-center justify-between', bgOnHover && 'hover:bg-gradient-to-r hover:from-white hover:to-grey-50', - separator ? 'border-y border-grey-100 last-of-type:border-t hover:border-grey-200' : 'border-y border-transparent hover:border-grey-200 first-of-type:hover:border-t-transparent', + separator ? 'border-b border-grey-100 last-of-type:border-b-transparent hover:border-grey-200' : 'border-y border-transparent hover:border-grey-200 first-of-type:hover:border-t-transparent', className ); return (
-
- {avatar && avatar} -
- {title} - {detail && {detail}} + {children ? children : +
+ {avatar && avatar} +
+ {title} + {detail && {detail}} +
-
+ } {action &&
{action} diff --git a/apps/admin-x-settings/src/admin-x-ds/global/chrome/DesktopChromeHeader.tsx b/apps/admin-x-settings/src/admin-x-ds/global/chrome/DesktopChromeHeader.tsx index bd2c6eda9e..79a32780d6 100644 --- a/apps/admin-x-settings/src/admin-x-ds/global/chrome/DesktopChromeHeader.tsx +++ b/apps/admin-x-settings/src/admin-x-ds/global/chrome/DesktopChromeHeader.tsx @@ -51,7 +51,7 @@ const DesktopChromeHeader: React.FC +
{toolbarLeft ?
{toolbarLeft} diff --git a/apps/admin-x-settings/src/admin-x-ds/global/form/Select.tsx b/apps/admin-x-settings/src/admin-x-ds/global/form/Select.tsx index 2b092f5c67..3dedd60822 100644 --- a/apps/admin-x-settings/src/admin-x-ds/global/form/Select.tsx +++ b/apps/admin-x-settings/src/admin-x-ds/global/form/Select.tsx @@ -96,10 +96,10 @@ const Select: React.FC = ({ ); return ( - unstyled ? select : + unstyled ? select : (title || hint ? (
{select} -
+
) : select) ); }; diff --git a/apps/admin-x-settings/src/admin-x-ds/global/form/TextField.tsx b/apps/admin-x-settings/src/admin-x-ds/global/form/TextField.tsx index deb400840f..6057fbcab2 100644 --- a/apps/admin-x-settings/src/admin-x-ds/global/form/TextField.tsx +++ b/apps/admin-x-settings/src/admin-x-ds/global/form/TextField.tsx @@ -20,6 +20,7 @@ export type TextFieldProps = React.InputHTMLAttributes & { containerClassName?: string; hintClassName?: string; unstyled?: boolean; + disabled?: boolean; } const TextField: React.FC = ({ @@ -39,6 +40,7 @@ const TextField: React.FC = ({ containerClassName = '', hintClassName = '', unstyled = false, + disabled, ...props }) => { const id = useId(); @@ -46,14 +48,16 @@ const TextField: React.FC = ({ const textFieldClasses = !unstyled && clsx( 'h-10 border-b py-2', clearBg ? 'bg-transparent' : 'bg-grey-75 px-[10px]', - error ? `border-red` : `border-grey-500 hover:border-grey-700 focus:border-black`, + error ? `border-red` : `${disabled ? 'border-grey-300' : 'border-grey-500 hover:border-grey-700 focus:border-black'}`, (title && !hideTitle && !clearBg) && `mt-2`, + (disabled ? 'text-grey-700' : ''), className ); const field = = ({ onChange={onChange} {...props} />; - return ( -
- {title && {title}} - {field} - {hint && {hint}} -
- ); + if (title || hint) { + return ( +
+ {title && {title}} + {field} + {hint && {hint}} +
+ ); + } else { + return field; + } }; export default TextField; diff --git a/apps/admin-x-settings/src/admin-x-ds/global/modal/Modal.stories.tsx b/apps/admin-x-settings/src/admin-x-ds/global/modal/Modal.stories.tsx index 9dfdf05fa9..919a3983bf 100644 --- a/apps/admin-x-settings/src/admin-x-ds/global/modal/Modal.stories.tsx +++ b/apps/admin-x-settings/src/admin-x-ds/global/modal/Modal.stories.tsx @@ -2,6 +2,7 @@ import type {Meta, StoryObj} from '@storybook/react'; import Modal from './Modal'; import ModalContainer from './ModalContainer'; +import ModalPage from './ModalPage'; import NiceModal from '@ebay/nice-modal-react'; const meta = { @@ -75,7 +76,7 @@ export const ExtraLarge: Story = { } }; -export const full: Story = { +export const Full: Story = { args: { size: 'full', onOk: () => { @@ -97,6 +98,19 @@ export const Bleed: Story = { } }; +export const CompletePage: Story = { + args: { + size: 'full', + footer: <>, + noPadding: true, + children: <> + +

This is a full page in a modal

+
+ + } +}; + export const CustomButtons: Story = { args: { leftButtonLabel: 'Extra action', diff --git a/apps/admin-x-settings/src/admin-x-ds/global/modal/ModalPage.stories.tsx b/apps/admin-x-settings/src/admin-x-ds/global/modal/ModalPage.stories.tsx new file mode 100644 index 0000000000..f3d8e31b0e --- /dev/null +++ b/apps/admin-x-settings/src/admin-x-ds/global/modal/ModalPage.stories.tsx @@ -0,0 +1,21 @@ +import type {Meta, StoryObj} from '@storybook/react'; + +import ModalPage from './ModalPage'; + +const meta = { + title: 'Global / Modal / Modal page contents', + component: ModalPage, + tags: ['autodocs'] +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + heading: 'Here\'s a modal page', + children: <> +

Use this component to in full-width or bleed modals in which you build a complete page (e.g. Theme grid)

+ + } +}; diff --git a/apps/admin-x-settings/src/admin-x-ds/global/modal/ModalPage.tsx b/apps/admin-x-settings/src/admin-x-ds/global/modal/ModalPage.tsx new file mode 100644 index 0000000000..547f2e2ce4 --- /dev/null +++ b/apps/admin-x-settings/src/admin-x-ds/global/modal/ModalPage.tsx @@ -0,0 +1,24 @@ +import Heading from '../Heading'; +import React from 'react'; +import clsx from 'clsx'; + +interface ModalPageProps { + heading?: string; + children?: React.ReactNode; + className?: string; +} + +const ModalPage: React.FC = ({heading, children, className}) => { + className = clsx( + 'min-h-full min-w-full p-[8vmin] pt-5', + className + ); + return ( +
+ {heading && {heading}} + {children} +
+ ); +}; + +export default ModalPage; \ No newline at end of file diff --git a/apps/admin-x-settings/src/admin-x-ds/global/modal/PreviewModal.tsx b/apps/admin-x-settings/src/admin-x-ds/global/modal/PreviewModal.tsx index 236e8e692a..98e4270b88 100644 --- a/apps/admin-x-settings/src/admin-x-ds/global/modal/PreviewModal.tsx +++ b/apps/admin-x-settings/src/admin-x-ds/global/modal/PreviewModal.tsx @@ -27,6 +27,7 @@ export interface PreviewModalProps { rightToolbar?: boolean; deviceSelector?: boolean; previewToolbarURLs?: SelectOption[]; + previewBgColor?: 'grey' | 'white'; selectedURL?: string; previewToolbarTabs?: Tab[]; defaultTab?: string; @@ -58,6 +59,7 @@ export const PreviewModalContent: React.FC = ({ rightToolbar = true, deviceSelector = true, previewToolbarURLs, + previewBgColor = 'grey', selectedURL, previewToolbarTabs, buttonsDisabled, @@ -137,7 +139,7 @@ export const PreviewModalContent: React.FC = ({ ); preview = ( - <> +
= ({ toolbarLeft={leftToolbar && toolbarLeft} toolbarRight={rightToolbar && toolbarRight} /> -
+
{preview}
- +
); } diff --git a/apps/admin-x-settings/src/components/settings/membership/PortalModal.tsx b/apps/admin-x-settings/src/components/settings/membership/PortalModal.tsx index f35aaf4478..18ba36e3b3 100644 --- a/apps/admin-x-settings/src/components/settings/membership/PortalModal.tsx +++ b/apps/admin-x-settings/src/components/settings/membership/PortalModal.tsx @@ -149,6 +149,7 @@ const PortalModal: React.FC = () => { dirty={saveState === 'unsaved'} okLabel='Save & close' preview={preview} + previewBgColor={selectedPreviewTab === 'links' ? 'white' : 'grey'} previewToolbarTabs={previewTabs} selectedURL={selectedPreviewTab} sidebar={sidebar} diff --git a/apps/admin-x-settings/src/components/settings/membership/portal/PortalLinks.tsx b/apps/admin-x-settings/src/components/settings/membership/portal/PortalLinks.tsx new file mode 100644 index 0000000000..cb794665cc --- /dev/null +++ b/apps/admin-x-settings/src/components/settings/membership/portal/PortalLinks.tsx @@ -0,0 +1,103 @@ +import Button from '../../../../admin-x-ds/global/Button'; +import List from '../../../../admin-x-ds/global/List'; +import ListItem from '../../../../admin-x-ds/global/ListItem'; +import ModalPage from '../../../../admin-x-ds/global/modal/ModalPage'; +import React, {useContext, useState} from 'react'; +import Select from '../../../../admin-x-ds/global/form/Select'; +import TextField from '../../../../admin-x-ds/global/form/TextField'; +import {SettingsContext} from '../../../providers/SettingsProvider'; +import {getHomepageUrl} from '../../../../utils/helpers'; + +interface PortalLinksPrefs { + +} + +interface PortalLinkPrefs { + name: string; + value: string; +} + +const PortalLink: React.FC = ({name, value}) => { + return ( + { + navigator.clipboard.writeText(value); + const button = e?.target as HTMLButtonElement; + button.innerText = 'Copied'; + setTimeout(() => { + button.innerText = 'Copy'; + }, 1000); + }}/>} + hideActions + separator + > +
+ {name} + +
+
+ ); +}; + +const PortalLinks: React.FC = () => { + const [isDataAttributes, setIsDataAttributes] = useState(false); + const {siteData} = useContext(SettingsContext); + + const toggleIsDataAttributes = () => { + setIsDataAttributes(!isDataAttributes); + }; + + const homePageURL = getHomepageUrl(siteData!); + + return ( + +

Use these {isDataAttributes ? 'data attributes' : 'links'} in your theme to show pages of Portal.

+ + } title='Generic'> + + + + + + + +
+ Tier +