Skip to content
Draft
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
Binary file added public/images/globeImage.png
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,119 @@
import { css, Global } from '@emotion/react';
import { useState, useEffect, useMemo } from 'react';
import ThemeProvider from '#app/components/ThemeProvider';
import { ServiceContextProvider } from '#app/contexts/ServiceContext';
import { AccountContext } from '#app/contexts/AccountContext';
import { ToggleContextProvider } from '#app/contexts/ToggleContext';
import AccountPromotionalBanner from '.';
import styles from './index.styles';

type AccountPromotionalBannerModalProps = {
isSignedIn: boolean;
onClose?: () => void;
};

const AccountPromotionalBannerModal = ({
isSignedIn,
onClose,
}: AccountPromotionalBannerModalProps) => {
const [isOpen, setIsOpen] = useState(true);

const handleClose = () => {
setIsOpen(false);
onClose?.();
};

useEffect(() => {
if (!isOpen) return undefined;

const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Escape') handleClose();
};

document.addEventListener('keydown', handleKeyDown);

return () => {
document.removeEventListener('keydown', handleKeyDown);
};
}, [isOpen]);

const accountContextValue = useMemo(
() => ({
isSignedIn,
isIdctaAvailable: true,
signInUrl: 'https://example.com/signin',
registerUrl: 'https://example.com/register',
signOutUrl: undefined,
settingsUrl: undefined,
forYouUrl: undefined,
isAccountPromoBannerVisible: true,
}),
[isSignedIn],
);

const handleBackdropKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'Enter' || e.key === ' ') handleClose();
};

if (!isOpen) {
return null;
}

return (
<>
<Global
styles={css`
body {
overflow: hidden;
}
`}
/>
<div
role="dialog"
aria-modal="true"
aria-label="Sign in to BBC"
css={styles.modal}
>
<div
role="button"
tabIndex={0}
aria-label="Close modal"
onClick={handleClose}
onKeyDown={handleBackdropKeyDown}
css={styles.backdrop}
/>
<div css={styles.modalContent}>
<div css={styles.modalInner}>
<div css={styles.modalImageSide}>
<img
src="/images/globeImageHorizontal.png"
alt=""
aria-hidden="true"
css={styles.imageHorizontal}
/>
<img
src="/images/globeImageVertical.png"
alt=""
aria-hidden="true"
css={styles.imageVertical}
/>
</div>
<div css={styles.modalBannerSide}>
<ToggleContextProvider>
<ThemeProvider service="ws">
<ServiceContextProvider service="ws">
<AccountContext.Provider value={accountContextValue}>
<AccountPromotionalBanner />
</AccountContext.Provider>
</ServiceContextProvider>
</ThemeProvider>
</ToggleContextProvider>
</div>
</div>
</div>
</div>
</>
);
};

export default AccountPromotionalBannerModal;
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { AccountContext } from '#app/contexts/AccountContext';
import README from './README.md';
import AccountPromotionalBanner from '.';
import { ToggleContextProvider } from '#app/contexts/ToggleContext';
import AccountPromotionalBannerModal from './AccountPromotionalModal';

type WithProvidersArgs = {
isSignedIn: boolean;
Expand Down Expand Up @@ -48,3 +49,6 @@ export default {

export const SignedOut = withProviders({ isSignedIn: false });
export const SignedInNoRender = withProviders({ isSignedIn: true });
export const SignedOutModal = () => (
<AccountPromotionalBannerModal isSignedIn={false} />
);
124 changes: 123 additions & 1 deletion src/app/components/Account/AccountPromotionalBanner/index.styles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Theme, css } from '@emotion/react';
import pixelsToRem from '#app/utilities/pixelsToRem';

export default {
callToActionLink: ({ mq }) =>
callToActionLink: ({ mq }: Theme) =>
css({
padding: '1rem',
display: 'inline-flex',
Expand Down Expand Up @@ -50,6 +50,7 @@ export default {
fill: 'ButtonText',
},
}),

registerLink: ({ palette }: Theme) =>
css({
height: `${pixelsToRem(44)}rem`,
Expand All @@ -67,4 +68,125 @@ export default {
color: palette.WHITE,
},
}),

modal: css({
position: 'fixed',
inset: 0,
zIndex: 2147483647,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: '100%',
height: '100%',
}),

backdrop: css({
position: 'absolute',
inset: 0,
backgroundColor: 'rgba(20, 20, 20, 0.9)',
backdropFilter: 'blur(0.2rem)',
}),

modalContent: ({ mq }: Theme) =>
css({
position: 'relative',
zIndex: 1,
width: '45%',
height: '50%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
overflow: 'hidden',

[mq.GROUP_4_MIN_WIDTH]: {
width: '60%',
height: '40%',
},

'& > aside': {
width: '100%',
height: '100%',
justifyContent: 'center',
alignItems: 'center',
},

'& > aside > div': {
justifyContent: 'center',
alignItems: 'center',
textAlign: 'center',
},

'& > aside > div > div': {
justifyContent: 'center',
alignItems: 'center',
},

'& > aside > div > div > div': {
paddingTop: '1rem',
paddingBottom: '1rem',
},
}),

modalInner: css({
position: 'relative',
display: 'flex',
flexDirection: 'column',
width: '100%',
height: '100%',
overflow: 'hidden',
}),

modalBannerSide: css({
flex: 1,
display: 'flex',
alignItems: 'stretch',
paddingInlineStart: '2rem',
}),

modalImageSide: ({ mq }: Theme) =>
css({
width: '100%',
height: '12rem',
overflow: 'hidden',
pointerEvents: 'none',
flexShrink: 0,

'& img': {
width: '100%',
height: '100%',
},

[mq.GROUP_4_MIN_WIDTH]: {
position: 'absolute',
insetInlineEnd: '5%',
top: '-20%',
width: '40%',
height: '140%',
zIndex: 2,
},
}),

imageHorizontal: ({ mq }: Theme) =>
css({
display: 'block',
width: '100%',
height: '100%',
objectFit: 'contain',

[mq.GROUP_4_MIN_WIDTH]: {
display: 'none',
},
}),

imageVertical: ({ mq }: Theme) =>
css({
display: 'none',
width: '100%',
height: '100%',
objectFit: 'contain',

[mq.GROUP_4_MIN_WIDTH]: {
display: 'block',
},
}),
};
20 changes: 17 additions & 3 deletions src/app/components/SaveArticleButton/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import useUASButton, { UASAction } from '#app/hooks/useUASButton';
import { useContext } from 'react';
import { ServiceContext } from '#contexts/ServiceContext';
import { useState, useContext } from 'react';
import { ServiceContext } from '#app/contexts/ServiceContext';
import { AccountContext } from '#app/contexts/AccountContext';
import { Article } from '#app/models/types/optimo';
import SaveButton from '../SaveButton';
import styles from './index.styles';
import AccountPromotionalBannerModal from '../Account/AccountPromotionalBanner/AccountPromotionalModal';

export interface SaveArticleButtonProps {
articleId: string;
Expand All @@ -25,9 +27,11 @@ const SaveArticleButton = ({

const { translations } = useContext(ServiceContext);
const { saveArticleButton } = translations || {};
const { isSignedIn } = useContext(AccountContext);

if (!showButton) return null;
const [isModalOpen, setIsModalOpen] = useState(false);

if (!showButton && isSignedIn) return null;
if (!saveArticleButton) return null;

if (error) {
Expand All @@ -45,6 +49,10 @@ const SaveArticleButton = ({
const buttonText = isLoading ? saveArticleButton.saving : buttonLabel;

const handleClick = () => {
if (!isSignedIn) {
setIsModalOpen(true);
return;
}
handleSaveAction(isSaved ? UASAction.REMOVE : UASAction.SAVE);
};

Expand All @@ -58,6 +66,12 @@ const SaveArticleButton = ({
buttonText={buttonText}
removeText={saveArticleButton.remove}
/>
{isModalOpen && (
<AccountPromotionalBannerModal
isSignedIn={false}
onClose={() => setIsModalOpen(false)}
/>
)}
</div>
);
};
Expand Down