Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

My Jetpack: Adding new modal interstitial #40945

Merged
merged 3 commits into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import { Text, Button, ThemeProvider, Col, Container } from '@automattic/jetpack-components';
import { Modal } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import clsx from 'clsx';
import { useCallback, useState, type FC } from 'react';
import styles from './style.module.scss';

interface ProductInterstitialModalProps {
title: string;
hideCloseButton?: boolean;
Copy link
Contributor

@IanRamosC IanRamosC Jan 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like we are not using this prop anywhere, so we can't actually hide the close button in the modal (isDismissible).

triggerButton?: React.ReactNode;
className?: string;
children?: React.ReactNode;
secondaryColumn?: React.ReactNode;
additionalColumn?: React.ReactNode;
onOpen?: () => void;
onClose?: () => void;
onClick?: () => void;
secondaryButtonExternalLink?: boolean;
secondaryButtonHref?: string;
buttonDisabled?: boolean;
buttonExternalLink?: boolean;
buttonHref?: string;
buttonContent?: string;
}

const ProductInterstitialModal: FC< ProductInterstitialModalProps > = props => {
const {
title,
className,
children,
triggerButton,
onOpen,
onClose,
onClick,
buttonDisabled,
buttonExternalLink,
buttonHref,
buttonContent,
secondaryButtonExternalLink,
secondaryButtonHref,
secondaryColumn,
additionalColumn = false,
} = props;

const [ isOpen, setOpen ] = useState( false );
const openModal = useCallback( () => {
onOpen?.();
setOpen( true );
}, [ onOpen ] );
const closeModal = useCallback( () => {
onClose?.();
setOpen( false );
}, [ onClose ] );

if ( ! title || ! children || ! triggerButton ) {
return null;
}

return (
<>
<ThemeProvider>
{
// TODO: use any component as a trigger
}
<Button variant="secondary" onClick={ openModal }>
{ triggerButton }
</Button>
{ isOpen && (
<Modal
onRequestClose={ closeModal }
className={ clsx( styles[ 'component-product-interstitial-modal' ], className ) }
>
<Container
className={ styles.wrapper }
horizontalSpacing={ 0 }
horizontalGap={ 1 }
fluid={ false }
>
{
// left column - always takes 33% of the width or the full with for small breakpoint
}
<Col sm={ 4 } md={ 8 } lg={ 4 } className={ styles.primary }>
<div className={ styles[ 'primary-content' ] }>
<div className={ styles.header }>
<Text variant="headline-small" className={ styles.title }>
{ title }
</Text>
</div>
{ children }
</div>
<div className={ styles[ 'primary-footer' ] }>
<Button
variant="primary"
className={ styles[ 'action-button' ] }
disabled={ buttonDisabled }
onClick={ onClick }
isExternalLink={ buttonExternalLink }
href={ buttonHref }
>
{ buttonContent }
</Button>
<Button
variant="link"
isExternalLink={ secondaryButtonExternalLink }
href={ secondaryButtonHref }
>
{ __( 'Learn more', 'jetpack-my-jetpack' ) }
</Button>
</div>
</Col>
{
// middle column for three columns layout and the right column for two columns layout
// small breakpoint: takes full width
// medium breakpoint: ~63% of the width without the additional column or 50% of the second row with the additional column
// large breakpoint: 66% of the width without the additional column or 33% with the additional column
}
<Col
sm={ 4 }
md={ additionalColumn ? 4 : 5 }
lg={ additionalColumn ? 4 : 8 }
className={ styles.secondary }
>
{ secondaryColumn }
</Col>
{
// additional column for three columns layout
// small breakpoint (max 4 cols): takes full width
// medium breakpoint (max 8 cols): 50% of the second row width
// large breakpoint (max 12 cols): 33% of the width
additionalColumn && (
<Col sm={ 4 } md={ 4 } lg={ 4 } className={ styles.additional }>
{ additionalColumn }
</Col>
)
}
</Container>
</Modal>
) }
</ThemeProvider>
</>
);
};

export default ProductInterstitialModal;
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { Text, ProductPrice } from '@automattic/jetpack-components';
import React from 'react';
import { HashRouter, Routes, Route } from 'react-router-dom';
import ProductInterstitialModal from '..';
import boostImage from './boost.png';

export default {
title: 'Packages/My Jetpack/Product Interstitial Modal',
component: ProductInterstitialModal,
};

const DefaultArgs = {
title: 'Product Interstitial Modal',
children: (
<div style={ { display: 'flex', flexDirection: 'column', gap: '1rem' } }>
<Text>
Lorem ipsum dolor <b>sit amet</b>, consectetur adipiscing elit. Cras rutrum neque odio, vel
viverra lectus vulputate et. Lorem ipsum dolor <b>sit amet</b>, consectetur adipiscing elit.
Cras rutrum neque odio, vel viverra lectus vulputate et. Lorem ipsum dolor <b>sit amet</b>,
consectetur adipiscing elit. Cras rutrum neque odio, vel viverra lectus vulputate et.
</Text>
<ProductPrice
currency="USD"
price={ 24.92 }
offPrice={ 12.42 }
showNotOffPrice={ true }
isNotConvenientPrice={ false }
hidePriceFraction={ false }
hideDiscountLabel={ false }
promoLabel="NEW"
legend="/month, paid yearly"
/>
</div>
),
triggerButton: 'Open Modal',
hideCloseButton: false,
buttonContent: 'Upgrade now',
secondaryColumn: <img src={ boostImage } alt="Boost" />,
buttonExternalLink: 'https://jetpack.com',
};

const Template = args => (
<HashRouter>
<Routes>
<Route path="/" element={ <ProductInterstitialModal { ...args } /> } />
</Routes>
</HashRouter>
);

export const Default = Template.bind( {} );

export const WithAdditionalColumn = Template.bind( {} );
WithAdditionalColumn.args = {
...DefaultArgs,
secondaryColumn: <div>CTA Content</div>,
additionalColumn: <div>Additional Column</div>,
};

Default.parameters = {};
Default.args = DefaultArgs;
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
.component-product-interstitial-modal {
:global(.components-modal__header) + div {
height: 100%;
}
}


.wrapper {
height: 100%;
}

.primary {
display: flex;
flex-direction: column;
justify-content: space-between;
}

.primary-content {
display: flex;
flex-direction: column;
justify-content: space-between;
}

.primary-footer {
display: flex;
flex-direction: row;
justify-content: space-between;
}

.secondary {
max-width: 100%;
height: auto;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;

img {
max-width: 100%;
height: auto;
max-height: 100%;
object-fit: contain;
display: flex;
align-items: center;
overflow: hidden;
}
}

.additional {
display: flex;
flex-direction: column;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: added

Adding new modal based interstitial component.
Loading