From b134bff778c12b7092d6000d73eb2f644582049c Mon Sep 17 00:00:00 2001 From: Tom Forbes Date: Tue, 16 Jan 2024 10:00:55 +0000 Subject: [PATCH] Remove epic components (#1038) * Remove epic components * remove test script * passWithNoTests * Add apple pay back in --- packages/modules/package.json | 4 +- .../modules/src/modules/epics/ApplePaySvg.tsx | 53 -- packages/modules/src/modules/epics/Button.tsx | 119 ----- .../src/modules/epics/ButtonApplePay.tsx | 72 --- .../src/modules/epics/BylineWithHeadshot.tsx | 93 ---- .../epics/ContributionsEpic.stories.tsx | 423 ---------------- .../modules/epics/ContributionsEpic.test.tsx | 89 ---- .../src/modules/epics/ContributionsEpic.tsx | 470 ----------------- ...picArticleCountAboveWithOptOut.stories.tsx | 45 -- ...butionsEpicArticleCountAboveWithOptOut.tsx | 475 ------------------ .../epics/ContributionsEpicButtons.tsx | 385 -------------- .../epics/ContributionsEpicChoiceCards.tsx | 208 -------- .../modules/epics/ContributionsEpicCtas.tsx | 92 ---- .../ContributionsEpicReminder.stories.tsx | 34 -- .../epics/ContributionsEpicReminder.tsx | 69 --- ...tributionsEpicReminderSignedIn.stories.tsx | 39 -- .../ContributionsEpicReminderSignedIn.tsx | 205 -------- ...ributionsEpicReminderSignedOut.stories.tsx | 39 -- .../ContributionsEpicReminderSignedOut.tsx | 217 -------- .../epics/ContributionsEpicSignInCta.tsx | 49 -- .../epics/ContributionsEpicTicker.stories.tsx | 32 -- .../modules/epics/ContributionsEpicTicker.tsx | 189 ------- .../ContributionsLiveblogEpic.stories.tsx | 54 -- .../ContributionsLiveblogEpic.testData.ts | 67 --- .../epics/ContributionsLiveblogEpic.tsx | 182 ------- .../src/modules/epics/NewsletterSignup.tsx | 58 --- .../src/modules/epics/PaymentCardsSvg.tsx | 95 ---- .../modules/src/modules/epics/utils/ophan.ts | 156 ------ .../src/modules/epics/utils/storybook.ts | 66 --- packages/server/src/api/epicRouter.ts | 12 - packages/shared/src/config/modules.ts | 9 - 31 files changed, 1 insertion(+), 4099 deletions(-) delete mode 100644 packages/modules/src/modules/epics/ApplePaySvg.tsx delete mode 100644 packages/modules/src/modules/epics/Button.tsx delete mode 100644 packages/modules/src/modules/epics/ButtonApplePay.tsx delete mode 100644 packages/modules/src/modules/epics/BylineWithHeadshot.tsx delete mode 100644 packages/modules/src/modules/epics/ContributionsEpic.stories.tsx delete mode 100644 packages/modules/src/modules/epics/ContributionsEpic.test.tsx delete mode 100644 packages/modules/src/modules/epics/ContributionsEpic.tsx delete mode 100644 packages/modules/src/modules/epics/ContributionsEpicArticleCountAboveWithOptOut.stories.tsx delete mode 100644 packages/modules/src/modules/epics/ContributionsEpicArticleCountAboveWithOptOut.tsx delete mode 100644 packages/modules/src/modules/epics/ContributionsEpicButtons.tsx delete mode 100644 packages/modules/src/modules/epics/ContributionsEpicChoiceCards.tsx delete mode 100644 packages/modules/src/modules/epics/ContributionsEpicCtas.tsx delete mode 100644 packages/modules/src/modules/epics/ContributionsEpicReminder.stories.tsx delete mode 100644 packages/modules/src/modules/epics/ContributionsEpicReminder.tsx delete mode 100644 packages/modules/src/modules/epics/ContributionsEpicReminderSignedIn.stories.tsx delete mode 100644 packages/modules/src/modules/epics/ContributionsEpicReminderSignedIn.tsx delete mode 100644 packages/modules/src/modules/epics/ContributionsEpicReminderSignedOut.stories.tsx delete mode 100644 packages/modules/src/modules/epics/ContributionsEpicReminderSignedOut.tsx delete mode 100644 packages/modules/src/modules/epics/ContributionsEpicSignInCta.tsx delete mode 100644 packages/modules/src/modules/epics/ContributionsEpicTicker.stories.tsx delete mode 100644 packages/modules/src/modules/epics/ContributionsEpicTicker.tsx delete mode 100644 packages/modules/src/modules/epics/ContributionsLiveblogEpic.stories.tsx delete mode 100644 packages/modules/src/modules/epics/ContributionsLiveblogEpic.testData.ts delete mode 100644 packages/modules/src/modules/epics/ContributionsLiveblogEpic.tsx delete mode 100644 packages/modules/src/modules/epics/NewsletterSignup.tsx delete mode 100644 packages/modules/src/modules/epics/PaymentCardsSvg.tsx delete mode 100644 packages/modules/src/modules/epics/utils/ophan.ts delete mode 100644 packages/modules/src/modules/epics/utils/storybook.ts diff --git a/packages/modules/package.json b/packages/modules/package.json index 79dec6e42..59efb0cb5 100644 --- a/packages/modules/package.json +++ b/packages/modules/package.json @@ -10,7 +10,7 @@ "lint": "eslint 'src/**/*.{ts,tsx}'", "storybook": "storybook dev -p 6006", "build-storybook": "storybook build -o storybook-static", - "test": "jest --silent" + "test": "jest --passWithNoTests" }, "dependencies": { "@babel/plugin-transform-react-jsx-compat": "^7.22.5", @@ -47,8 +47,6 @@ "@storybook/addon-links": "^7.1.1", "@storybook/react": "^7.1.1", "@storybook/react-webpack5": "^7.1.1", - "@testing-library/jest-dom": "^5.5.0", - "@testing-library/react": "^10.0.3", "@types/jest": "^29.5.6", "@types/react-dom": "18.0.11", "babel-loader": "^9.1.3", diff --git a/packages/modules/src/modules/epics/ApplePaySvg.tsx b/packages/modules/src/modules/epics/ApplePaySvg.tsx deleted file mode 100644 index dc3680249..000000000 --- a/packages/modules/src/modules/epics/ApplePaySvg.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import React from 'react'; -import { css, SerializedStyles } from '@emotion/react'; -import { space } from '@guardian/src-foundations'; -import { from } from '@guardian/src-foundations/mq'; - -const applePayStyles = css` - display: block; - height: 1.1rem; - width: auto; - margin-top: ${space[2]}px; - - ${from.tablet} { - height: 1.25rem; - } -`; - -type ApplePayProps = { - cssOverrides?: SerializedStyles; -}; - -export const ApplePaySvg = ({ cssOverrides }: ApplePayProps): JSX.Element => { - return ( - - - - - - - - ); -}; diff --git a/packages/modules/src/modules/epics/Button.tsx b/packages/modules/src/modules/epics/Button.tsx deleted file mode 100644 index 0364233ba..000000000 --- a/packages/modules/src/modules/epics/Button.tsx +++ /dev/null @@ -1,119 +0,0 @@ -import React from 'react'; -import { SerializedStyles, css } from '@emotion/react'; -import { palette } from '@guardian/src-foundations'; -import { ThemeProvider } from '@emotion/react'; -import { Button as DSButton, LinkButton } from '@guardian/src-button'; -import { SvgArrowRightStraight } from '@guardian/src-icons'; -import type { ReactComponent } from '../../types'; -import { - OPHAN_COMPONENT_EVENT_PRIMARY_CTA, - OPHAN_COMPONENT_EVENT_SECONDARY_CTA, -} from './utils/ophan'; -import { OphanComponentEvent } from '@sdc/shared/src/types'; - -// Custom theme for Button/LinkButton -// See also `tertiaryButtonOverrides` below. -const buttonStyles = { - textPrimary: palette.neutral[7], - backgroundPrimary: palette.brandAlt[400], - backgroundPrimaryHover: palette.brandAlt[300], - textSecondary: palette.neutral[7], - backgroundSecondary: palette.neutral[93], - backgroundSecondaryHover: palette.neutral[86], - borderSecondary: palette.neutral[86], -}; - -const contributionsTheme = { - button: buttonStyles, - link: buttonStyles, -}; - -type Url = string; - -type Props = { - // Accept a function or a string; - // A function will render a - - - - ) : ( - <> -
- Many readers tell us they enjoy seeing how many pieces of - Guardian journalism they’ve read, watched or listened to. Can we - start showing you your article count on support appeals like - this? -
-
- - -
- - )} - -
- To opt out of other tracking activity, manage your{' '} - - Privacy Settings - -
- - )} - - ); -}; - -// --- Helper components --- // - -interface ArticleCountWithToggleProps { - articleCount: number; - isArticleCountOn: boolean; - onToggleClick: () => void; - copy?: string; -} - -const ArticleCountWithToggle: ReactComponent = ({ - isArticleCountOn, - articleCount, - onToggleClick, - copy, -}: ArticleCountWithToggleProps) => { - if (isArticleCountOn && articleCount >= 5) { - return ( -
- - -
-
Article count
- - on - -
-
- ); - } - - if (!isArticleCountOn) { - return ( -
-
Article count
- - off - -
- ); - } - - return <>; -}; - -const ARTICLE_COUNT_TEMPLATE = '%%ARTICLE_COUNT%%'; -const containsArticleCountTemplate = (copy: string): boolean => - copy.includes(ARTICLE_COUNT_TEMPLATE); - -interface CustomArticleCountCopyProps { - articleCount: number; - copy: string; -} - -const CustomArticleCountCopy: ReactComponent = ({ - articleCount, - copy, -}) => { - const [copyHead, copyTail] = copy.split(ARTICLE_COUNT_TEMPLATE); - - return ( -
- {copyHead} - {articleCount} articles - {copyTail.substring(1, 9) === 'articles' ? copyTail.substring(9) : copyTail} -
- ); -}; - -interface ArticleCountProps { - articleCount: number; - copy?: string; -} - -const ArticleCount: ReactComponent = ({ articleCount, copy }) => { - if (copy && containsArticleCountTemplate(copy)) { - // Custom article count message - return ; - } else if (articleCount >= 50) { - return ( -
- Congratulations on being one of our top readers globally – you've read{' '} - {articleCount} articles in the last year -
- ); - } else { - return ( -
- You've read {articleCount} articles in the - last year -
- ); - } -}; - -// --- Styles --- // - -const topContainer = css` - display: flex; - flex-direction: column-reverse; - - ${from.tablet} { - display: block; - margin-top: 10px; - } -`; - -const articleCountAboveContainerStyles = css` - font-style: italic; - ${body.small({ fontWeight: 'bold' })}; - - ${from.tablet} { - ${body.medium({ fontWeight: 'bold' })}; - } -`; - -const optOutContainer = css` - color: ${palette.opinion[400]}; -`; - -const articleCountOnHeaderContainerStyles = css` - display: flex; - justify-content: space-between; - flex-direction: column-reverse; - align-items: flex-start; - - ${from.tablet} { - flex-direction: row; - align-items: flex-start; - } -`; - -const articleCountWrapperStyles = css` - flex-shrink: 0; - display: flex; - flex-direction: row; - align-items: flex-start; - margin-right: ${space[2]}px; - margin-bottom: ${space[2]}px; - justify-content: start; - - ${from.tablet} { - margin-left: ${space[5]}px; - margin-bottom: 0; - justify-content: flex-end; - } -`; - -const articleCountTextStyles = css` - ${textSans.xxsmall()}; - margin-right: ${space[1]}px; - - ${from.tablet} { - ${textSans.small()}; - } -`; - -const articleCountCtaStyles = css` - margin-top: 0; - - ${textSans.xxsmall({ fontWeight: 'bold' })}; - - ${from.tablet} { - ${textSans.small({ fontWeight: 'bold' })}; - } -`; - -const articleCountDescriptionTopContainerStyles = css` - border-bottom: 1px solid ${palette.neutral[46]}; - position: relative; - margin-bottom: ${space[2]}px; - - ${from.tablet} { - margin-top: ${space[4]}px; - border-top: 1px solid ${palette.neutral[0]}; - border-bottom: 1px solid ${palette.neutral[0]}; - } -`; - -const articleCountDescriptionContainer = css` - align-items: center; - display: flex; - flex-direction: column; - padding: ${space[1]}px ${space[1]}px 0; - - ${from.tablet} { - flex-direction: row; - padding: ${space[1]}px 0; - align-items: start; - margin-top: ${space[1]}px; - } -`; - -const articleCountBodyTextStyles = css` - ${textSans.small()}; - width: 100%; - - ${from.tablet} { - width: 68%; - } -`; - -const articleCountCtasContainerStyles = css` - display: flex; - align-self: start; - margin-top: ${space[4]}px; - > * + * { - margin-left: ${space[3]}px; - } - - ${from.tablet} { - flex-direction: column; - margin-left: auto; - margin-top: ${space[2]}px; - justify-content: space-between; - > * + * { - margin-top: ${space[3]}px; - margin-left: 0; - } - } -`; - -const articleCountOptInCtaStyles = css` - background-color: ${palette.neutral[0]}; -`; - -const articleCountDefaultCtaStyles = css` - background-color: ${palette.neutral[0]}; - padding: auto ${space[6]}px; - - ${from.tablet} { - padding-left: ${space[5]}px; - } -`; - -const articleCountOptOutCtaStyles = css` - color: ${palette.neutral[0]}; - border: 1px solid ${palette.neutral[0]}; -`; - -const trackingSettingsContainerStyles = css` - margin: ${space[4]}px auto ${space[3]}px; - ${textSans.xxsmall()}; - - ${from.tablet} { - ${textSans.xsmall()}; - } -`; - -const privacySettingsLinkStyles = css` - ${textSans.xxsmall({ fontWeight: 'bold' })}; - - ${from.tablet} { - ${textSans.xsmall({ fontWeight: 'bold' })}; - } -`; - -const caretStyles = css` - &:before { - content: ''; - display: block; - position: absolute; - bottom: -14px; - width: 0; - height: 0; - border: 7px solid transparent; - border-top-color: ${palette.neutral[46]}; - - ${from.tablet} { - right: 5px; - bottom: 100%; - border: 10px solid transparent; - border-bottom-color: ${palette.neutral[0]}; - } - - ${until.tablet} { - left: 75px; - } - } - - &:after { - content: ''; - display: block; - position: absolute; - bottom: -12px; - width: 0; - height: 0; - border: 6px solid transparent; - border-top-color: ${palette.neutral[97]}; - - ${from.tablet} { - right: 6px; - bottom: 100%; - border: 9px solid transparent; - border-bottom-color: ${palette.neutral[97]}; - } - - ${until.tablet} { - left: 76px; - } - } -`; diff --git a/packages/modules/src/modules/epics/ContributionsEpicButtons.tsx b/packages/modules/src/modules/epics/ContributionsEpicButtons.tsx deleted file mode 100644 index ba06e8ef6..000000000 --- a/packages/modules/src/modules/epics/ContributionsEpicButtons.tsx +++ /dev/null @@ -1,385 +0,0 @@ -import React, { useEffect } from 'react'; -import { SerializedStyles, css } from '@emotion/react'; -import { from } from '@guardian/src-foundations/mq'; -import { palette, space } from '@guardian/src-foundations'; -import { Button } from './Button'; -import { EpicVariant, SecondaryCtaType, Tracking, Cta } from '@sdc/shared/types'; -import { addRegionIdAndTrackingParamsToSupportUrl } from '@sdc/shared/lib'; -import { OphanComponentEvent } from '@sdc/shared/types'; -import { - getReminderViewEvent, - OPHAN_COMPONENT_EVENT_APPLEPAY_VIEW, - OPHAN_COMPONENT_EVENT_CTAS_VIEW, - OPHAN_COMPONENT_EVENT_REMINDER_OPEN, -} from './utils/ophan'; -import { useHasBeenSeen } from '../../hooks/useHasBeenSeen'; -import { hasSetReminder } from '../utils/reminders'; -import { isSupportUrl } from '@sdc/shared/dist/lib'; -import { PaymentCardSvg } from './PaymentCardsSvg'; -import { ButtonApplePay } from './ButtonApplePay'; -import { ChoiceCardSelection } from '../shared/helpers/choiceCards'; - -const paymentImageStyles = css` - display: inline-block; - width: auto; - height: 25px; - margin: ${space[1]}px 0; -`; - -const svgPositionStyles = css` - margin: ${space[1]}px -${space[2]}px; -`; - -const buttonWrapperStyles = (showApplePay?: boolean): SerializedStyles => css` - margin: ${space[6]}px ${space[2]}px ${space[1]}px 0; - display: flex; - ${showApplePay - ? `flex-direction: column; margin-right: 0px; ${from.desktop} {flex-direction: row;}` - : `flex-wrap: wrap; align-items: center;`} - - &.hidden { - display: none; - } -`; - -const buttonMarginStyles = (showApplePay?: boolean): SerializedStyles => css` - ${showApplePay - ? `margin: ${space[1]}px; ${from.desktop} {width: 100%;}` - : `margin: ${space[1]}px ${space[2]}px ${space[1]}px 0;`}; -`; - -const ApplePayButtonOverrides = css` - width: 100%; - justify-content: center; - padding: 0 10px; - border: 1px solid ${palette.neutral[0]} !important; - background-color: ${palette.neutral[93]} !important; - color: ${palette.neutral[7]} !important; - - ${from.mobileMedium} { - padding: 0 20px; - } - - :hover { - background-color: ${palette.neutral[86]} !important; - } - - svg { - width: 140px; - } -`; - -const reminderDesktopHideStyles = (showApplePay?: boolean): SerializedStyles => css` - ${showApplePay ? `${from.desktop} {display: none;}` : ``}; -`; - -const PrimaryCtaButton = ({ - cta, - tracking, - countryCode, - amountsTestName, - amountsVariantName, - numArticles, - submitComponentEvent, -}: { - cta?: Cta; - tracking: Tracking; - countryCode?: string; - amountsTestName?: string; - amountsVariantName?: string; - numArticles: number; - submitComponentEvent?: (event: OphanComponentEvent) => void; -}): JSX.Element | null => { - if (!cta) { - return null; - } - - const buttonText = cta.text || 'Support The Guardian'; - const baseUrl = cta.baseUrl || 'https://support.theguardian.com/contribute'; - const urlWithRegionAndTracking = addRegionIdAndTrackingParamsToSupportUrl( - baseUrl, - tracking, - numArticles, - countryCode, - amountsTestName, - amountsVariantName, - ); - - return ( -
- -
- ); -}; - -const SecondaryCtaButton = ({ - cta, - tracking, - numArticles, - countryCode, - submitComponentEvent, -}: { - cta: Cta; - tracking: Tracking; - countryCode?: string; - numArticles: number; - submitComponentEvent?: (event: OphanComponentEvent) => void; -}): JSX.Element | null => { - const url = addRegionIdAndTrackingParamsToSupportUrl( - cta.baseUrl, - tracking, - numArticles, - countryCode, - ); - return ( -
- -
- ); -}; - -const PrimaryCtaButtonApplePay = ({ - cta, - tracking, - countryCode, - amountsTestName, - amountsVariantName, - numArticles, - submitComponentEvent, -}: { - cta?: Cta; - tracking: Tracking; - countryCode?: string; - amountsTestName?: string; - amountsVariantName?: string; - numArticles: number; - submitComponentEvent?: (event: OphanComponentEvent) => void; -}): JSX.Element | null => { - if (!cta) { - return null; - } - - const buttonText = 'Support with'; - const baseUrl = cta.baseUrl || 'https://support.theguardian.com/contribute'; - const urlWithRegionAndTracking = addRegionIdAndTrackingParamsToSupportUrl( - baseUrl, - tracking, - numArticles, - countryCode, - amountsTestName, - amountsVariantName, - ); - - return ( -
- - {buttonText} - -
- ); -}; - -const SecondaryCtaButtonApplePay = ({ - cta, - tracking, - numArticles, - countryCode, - submitComponentEvent, -}: { - cta: Cta; - tracking: Tracking; - countryCode?: string; - numArticles: number; - submitComponentEvent?: (event: OphanComponentEvent) => void; -}): JSX.Element | null => { - const buttonText = 'Support with'; - const url = addRegionIdAndTrackingParamsToSupportUrl( - cta.baseUrl, - tracking, - numArticles, - countryCode, - ); - return ( -
- -
- ); -}; - -interface ContributionsEpicButtonsProps { - variant: EpicVariant; - tracking: Tracking; - countryCode?: string; - onOpenReminderClick: () => void; - submitComponentEvent?: (event: OphanComponentEvent) => void; - isReminderActive: boolean; - isSignedIn: boolean; - showChoiceCards?: boolean; - amountsTestName?: string; - amountsVariantName?: string; - choiceCardSelection?: ChoiceCardSelection; - numArticles: number; - showApplePayButton?: boolean; -} - -export const ContributionsEpicButtons = ({ - variant, - tracking, - countryCode, - onOpenReminderClick, - submitComponentEvent, - isReminderActive, - isSignedIn, - showChoiceCards, - choiceCardSelection, - amountsTestName, - amountsVariantName, - numArticles, - showApplePayButton, -}: ContributionsEpicButtonsProps): JSX.Element | null => { - const [hasBeenSeen, setNode] = useHasBeenSeen({}, true); - const { cta, secondaryCta, showReminderFields } = variant; - - if (!cta) { - return null; - } - - const getCta = (cta: Cta): Cta => - showChoiceCards && choiceCardSelection - ? { - text: cta.text, - baseUrl: `${cta.baseUrl}?selected-contribution-type=${choiceCardSelection.frequency}&selected-amount=${choiceCardSelection.amount}`, - } - : cta; - - useEffect(() => { - if (hasBeenSeen && submitComponentEvent) { - submitComponentEvent(OPHAN_COMPONENT_EVENT_CTAS_VIEW); - if (showApplePayButton) { - submitComponentEvent(OPHAN_COMPONENT_EVENT_APPLEPAY_VIEW); - } - if (showReminderFields && !hasSetReminder()) { - submitComponentEvent(getReminderViewEvent(isSignedIn)); - } - } - }, [hasBeenSeen]); - - const openReminder = () => { - if (submitComponentEvent) { - submitComponentEvent(OPHAN_COMPONENT_EVENT_REMINDER_OPEN); - } - onOpenReminderClick(); - }; - - const hasSupportCta = - isSupportUrl(cta.baseUrl) || - (secondaryCta?.type === SecondaryCtaType.Custom && isSupportUrl(secondaryCta.cta.baseUrl)); - - return ( -
- {!isReminderActive && ( - <> - {showApplePayButton ? ( - <> - - - - ) : ( - <> - - {secondaryCta?.type === SecondaryCtaType.Custom && - secondaryCta.cta.baseUrl && - secondaryCta.cta.text && ( - - )} - - )} - - {secondaryCta?.type === SecondaryCtaType.ContributionsReminder && - showReminderFields && - !hasSetReminder() && ( -
- -
- )} - - {hasSupportCta && !showApplePayButton && ( - Accepted payment methods: Visa, Mastercard, American Express and PayPal - )} - - )} -
- ); -}; diff --git a/packages/modules/src/modules/epics/ContributionsEpicChoiceCards.tsx b/packages/modules/src/modules/epics/ContributionsEpicChoiceCards.tsx deleted file mode 100644 index 8dd2e2bb5..000000000 --- a/packages/modules/src/modules/epics/ContributionsEpicChoiceCards.tsx +++ /dev/null @@ -1,208 +0,0 @@ -import React, { useEffect } from 'react'; -import { ChoiceCardGroup, ChoiceCard } from '@guardian/src-choice-card'; -import { - ContributionFrequency, - SelectedAmountsVariant, - contributionTabFrequencies, - OphanComponentEvent, -} from '@sdc/shared/types'; -import { css } from '@emotion/react'; -import { until } from '@guardian/src-foundations/mq'; -import { visuallyHidden } from '@guardian/src-foundations/accessibility'; -import { HasBeenSeen, useHasBeenSeen } from '../../hooks/useHasBeenSeen'; -import { contributionType, ChoiceCardSelection } from '../shared/helpers/choiceCards'; -import type { ReactComponent } from '../../types'; - -// CSS Styling -// ------------------------------------------- -const frequencyChoiceCardGroupOverrides = css` - ${until.mobileLandscape} { - > div { - display: flex !important; - } - - > div label:nth-of-type(2) { - margin-left: 4px !important; - margin-right: 4px !important; - } - } -`; - -const hideChoiceCardGroupLegend = css` - legend { - ${visuallyHidden}; - } -`; - -// This `position: relative` is necessary to stop it jumping to the top of the page when a button is clicked -const container = css` - position: relative; -`; - -// ContributionsEpicChoiceCards - exported component -// ------------------------------------------- -interface EpicChoiceCardProps { - selection?: ChoiceCardSelection; - setSelectionsCallback: (choiceCardSelection: ChoiceCardSelection) => void; - submitComponentEvent?: (event: OphanComponentEvent) => void; - currencySymbol: string; - amountsTest: SelectedAmountsVariant; -} - -export const ContributionsEpicChoiceCards: ReactComponent = ({ - selection, - setSelectionsCallback, - submitComponentEvent, - currencySymbol, - amountsTest, -}: EpicChoiceCardProps) => { - if (!selection || !amountsTest) { - return <>; - } - - const { - testName = 'test_undefined', - variantName = 'variant_undefined', - displayContributionType = contributionTabFrequencies, - amountsCardData, - } = amountsTest; - - if (!amountsCardData) { - return <>; - } - - const [hasBeenSeen, setNode] = useHasBeenSeen({ threshold: 0 }, true) as HasBeenSeen; - - useEffect(() => { - if (hasBeenSeen) { - // For ophan - if (submitComponentEvent) { - submitComponentEvent({ - component: { - componentType: 'ACQUISITIONS_OTHER', - id: 'contributions-epic-choice-cards', - }, - action: 'VIEW', - abTest: { - name: testName, - variant: variantName, - }, - }); - } - } - }, [hasBeenSeen, submitComponentEvent]); - - const trackClick = (type: 'amount' | 'frequency'): void => { - if (submitComponentEvent) { - submitComponentEvent({ - component: { - componentType: 'ACQUISITIONS_OTHER', - id: `contributions-epic-choice-cards-change-${type}`, - }, - action: 'CLICK', - }); - } - }; - - const updateAmount = (amount: number | 'other') => { - trackClick('amount'); - setSelectionsCallback({ - frequency: selection.frequency, - amount: amount, - }); - }; - - const updateFrequency = (frequency: ContributionFrequency) => { - trackClick('frequency'); - setSelectionsCallback({ - frequency: frequency, - amount: amountsCardData[frequency].defaultAmount, - }); - }; - - const ChoiceCardAmount = ({ amount }: { amount?: number }) => { - if (amount) { - return ( - updateAmount(amount)} - /> - ); - } - return null; - }; - - const generateChoiceCardAmountsButtons = () => { - const productData = amountsCardData[selection.frequency]; - const requiredAmounts = productData.amounts; - const hideChooseYourAmount = productData.hideChooseYourAmount ?? false; - - // Something is wrong with the data - if (!Array.isArray(requiredAmounts) || !requiredAmounts.length) { - return ( - - ); - } - - return ( - <> - - - {hideChooseYourAmount ? ( - - ) : ( - updateAmount('other')} - /> - )} - - ); - }; - - const generateChoiceCardFrequencyTab = (frequency: ContributionFrequency) => { - return ( - updateFrequency(frequency)} - /> - ); - }; - - return ( -
-
- - {displayContributionType.map((f) => generateChoiceCardFrequencyTab(f))} - -
- - {generateChoiceCardAmountsButtons()} - -
- ); -}; diff --git a/packages/modules/src/modules/epics/ContributionsEpicCtas.tsx b/packages/modules/src/modules/epics/ContributionsEpicCtas.tsx deleted file mode 100644 index 6fdb4a5df..000000000 --- a/packages/modules/src/modules/epics/ContributionsEpicCtas.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import React, { useState } from 'react'; -import { EpicProps } from '@sdc/shared/types'; -import { ContributionsEpicReminder } from './ContributionsEpicReminder'; -import { ContributionsEpicButtons } from './ContributionsEpicButtons'; -import { ChoiceCardSelection } from '../shared/helpers/choiceCards'; -import type { ReactComponent } from '../../types'; - -interface OnReminderOpen { - buttonCopyAsString: string; -} - -type ContributionsEpicCtasProps = EpicProps & { - showApplePayButton?: boolean; - showChoiceCards?: boolean; - choiceCardSelection?: ChoiceCardSelection; - amountsTestName?: string; - amountsVariantName?: string; -}; - -export const ContributionsEpicCtas: ReactComponent = ({ - variant, - countryCode, - articleCounts, - tracking, - submitComponentEvent, - onReminderOpen, - fetchEmail, - showApplePayButton, - showChoiceCards, - choiceCardSelection, - amountsTestName, - amountsVariantName, -}: ContributionsEpicCtasProps): JSX.Element => { - const [fetchedEmail, setFetchedEmail] = useState(undefined); - const [isReminderActive, setIsReminderActive] = useState(false); - const showReminderFields = variant.showReminderFields; - const onCloseReminderClick = () => { - setIsReminderActive(false); - }; - - return ( - <> - { - const buttonCopyAsString = showReminderFields?.reminderCta - .toLowerCase() - .replace(/\s/g, '-'); - - // This callback lets the platform react to the user interaction with the - // 'Remind me' button - if (onReminderOpen) { - onReminderOpen({ - buttonCopyAsString, - } as OnReminderOpen); - } - - if (fetchEmail) { - fetchEmail().then((resolvedEmail) => { - if (resolvedEmail) { - setFetchedEmail(resolvedEmail); - } - setIsReminderActive(true); - }); - } else { - setIsReminderActive(true); - } - }} - submitComponentEvent={submitComponentEvent} - isReminderActive={isReminderActive} - isSignedIn={Boolean(fetchedEmail)} - showApplePayButton={showApplePayButton} - showChoiceCards={showChoiceCards} - choiceCardSelection={choiceCardSelection} - amountsTestName={amountsTestName} - amountsVariantName={amountsVariantName} - numArticles={articleCounts.for52Weeks} - /> - - {isReminderActive && showReminderFields && ( - - )} - - ); -}; diff --git a/packages/modules/src/modules/epics/ContributionsEpicReminder.stories.tsx b/packages/modules/src/modules/epics/ContributionsEpicReminder.stories.tsx deleted file mode 100644 index 08d1fd105..000000000 --- a/packages/modules/src/modules/epics/ContributionsEpicReminder.stories.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React from 'react'; -import { Story, Meta } from '@storybook/react'; -import { - ContributionsEpicReminder, - ContributionsEpicReminderProps, -} from './ContributionsEpicReminder'; -import { EpicDecorator } from './ContributionsEpic.stories'; - -export default { - component: ContributionsEpicReminder, - title: 'Epics/ContributionsEpicReminder', - args: { - reminderFields: { - reminderCta: 'Remind me in May', - reminderLabel: 'May', - reminderPeriod: '2021-05-01', - }, - onCloseReminderClick: () => { - console.log('closed'); - }, - }, - decorators: [EpicDecorator], -} as Meta; - -const Template: Story = (props: ContributionsEpicReminderProps) => ( - -); - -export const SignedIn = Template.bind({}); -SignedIn.args = { - initialEmailAddress: 'test.user@guardian.co.uk', -}; - -export const SignedOut = Template.bind({}); diff --git a/packages/modules/src/modules/epics/ContributionsEpicReminder.tsx b/packages/modules/src/modules/epics/ContributionsEpicReminder.tsx deleted file mode 100644 index f227b52fa..000000000 --- a/packages/modules/src/modules/epics/ContributionsEpicReminder.tsx +++ /dev/null @@ -1,69 +0,0 @@ -// --- Imports --- // - -import React from 'react'; -import { ContributionsEpicReminderSignedIn } from './ContributionsEpicReminderSignedIn'; -import { ContributionsEpicReminderSignedOut } from './ContributionsEpicReminderSignedOut'; -import { OphanComponentEvent } from '@sdc/shared/types'; -import { - OPHAN_COMPONENT_EVENT_REMINDER_CLOSE, - OPHAN_COMPONENT_EVENT_REMINDER_SET, -} from './utils/ophan'; -import { useContributionsReminderSignup } from '../../hooks/useContributionsReminderSignup'; -import { ReminderFields } from '@sdc/shared/dist/lib'; -import type { ReactComponent } from '../../types'; - -// --- Types --- // - -export interface ContributionsEpicReminderProps { - initialEmailAddress?: string; - reminderFields: ReminderFields; - onCloseReminderClick: () => void; - submitComponentEvent?: (event: OphanComponentEvent) => void; -} - -// --- Components --- // - -export const ContributionsEpicReminder: ReactComponent = ({ - initialEmailAddress, - reminderFields, - onCloseReminderClick, - submitComponentEvent, -}: ContributionsEpicReminderProps) => { - const { reminderStatus, createReminder } = useContributionsReminderSignup( - reminderFields.reminderPeriod, - 'WEB', - 'EPIC', - 'PRE', - reminderFields.reminderOption, - ); - - const onSetReminderClick = (email: string) => { - if (submitComponentEvent) { - submitComponentEvent(OPHAN_COMPONENT_EVENT_REMINDER_SET); - } - createReminder(email); - }; - - const closeReminder = () => { - if (submitComponentEvent) { - submitComponentEvent(OPHAN_COMPONENT_EVENT_REMINDER_CLOSE); - } - onCloseReminderClick(); - }; - - return initialEmailAddress ? ( - onSetReminderClick(initialEmailAddress)} - onCloseReminderClick={closeReminder} - /> - ) : ( - - ); -}; diff --git a/packages/modules/src/modules/epics/ContributionsEpicReminderSignedIn.stories.tsx b/packages/modules/src/modules/epics/ContributionsEpicReminderSignedIn.stories.tsx deleted file mode 100644 index 8d421e3ee..000000000 --- a/packages/modules/src/modules/epics/ContributionsEpicReminderSignedIn.stories.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import React from 'react'; -import { Story, Meta } from '@storybook/react'; -import { - ContributionsEpicReminderSignedIn, - ContributionsEpicReminderSignedInProps, -} from './ContributionsEpicReminderSignedIn'; -import { ReminderStatus } from '../utils/reminders'; -import { EpicDecorator } from './ContributionsEpic.stories'; - -export default { - component: ContributionsEpicReminderSignedIn, - title: 'Epics/ContributionsEpicReminderSignedIn', - args: { - reminderLabel: 'May', - reminderStatus: ReminderStatus.Editing, - }, - decorators: [EpicDecorator], -} as Meta; - -const Template: Story = ( - props: ContributionsEpicReminderSignedInProps, -) => ; - -export const Default = Template.bind({}); - -export const Submitting = Template.bind({}); -Submitting.args = { - reminderStatus: ReminderStatus.Submitting, -}; - -export const Completed = Template.bind({}); -Completed.args = { - reminderStatus: ReminderStatus.Completed, -}; - -export const Error = Template.bind({}); -Error.args = { - reminderStatus: ReminderStatus.Error, -}; diff --git a/packages/modules/src/modules/epics/ContributionsEpicReminderSignedIn.tsx b/packages/modules/src/modules/epics/ContributionsEpicReminderSignedIn.tsx deleted file mode 100644 index d203ae271..000000000 --- a/packages/modules/src/modules/epics/ContributionsEpicReminderSignedIn.tsx +++ /dev/null @@ -1,205 +0,0 @@ -// --- Imports --- // - -import React from 'react'; -import { css } from '@emotion/react'; -import { headline, textSans, body } from '@guardian/src-foundations/typography'; -import { palette, space } from '@guardian/src-foundations'; -import { from } from '@guardian/src-foundations/mq'; -import { Lines } from '../shared/Lines'; -import { Button } from '@guardian/src-button'; -import { Hide } from '@guardian/src-layout'; -import { SvgCheckmark, SvgCross } from '@guardian/src-icons'; -import { ensureHasPreposition, ReminderStatus } from '../utils/reminders'; -import type { ReactComponent } from '../../types'; - -// --- Styles --- // - -const rootStyles = css` - position: relative; - - background-color: ${palette.neutral[97]}; - - * { - box-sizing: border-box; - } - - p { - margin-top: 0; - margin-bottom: 0; - } -`; - -const lineWrapperStyles = css` - margin: ${space[3]}px auto; -`; - -const remindHeading = css` - ${headline.xxsmall({ fontWeight: 'bold' })}; - margin: 0 ${space[5]}px ${space[2]}px 0; -`; - -const successTextStyles = css` - margin: 0 auto ${space[2]}px; - ${body.medium()}; -`; - -const linkStyles = css` - color: ${palette.neutral[7]}; -`; - -const errorTextStyles = css` - ${textSans.small({ fontWeight: 'bold' })}; - color: ${palette.error[400]}; - font-style: italic; - margin-top: ${space[2]}px !important; - margin-bottom: 0; -`; - -const closeButtonContainerStyles = css` - display: none; - position: absolute; - top: 20px; - right: 0; - - ${from.tablet} { - display: block; - } -`; - -const bodyCopyStyles = css` - ${body.medium()} - - ${from.tablet} { - margin-right: ${space[9]}px; - } -`; - -const infoCopyStyles = css` - ${textSans.small()}; - font-style: italic; - margin-top: ${space[2]}px !important; -`; - -const ctaContainerStyles = css` - display: flex; - flex-direction: row; - align-items: center; - margin-top: ${space[4]}px; - - & > * + * { - margin-left: ${space[6]}px; - } -`; - -// --- Types --- // - -export interface ContributionsEpicReminderSignedInProps { - reminderLabel: string; - reminderStatus: ReminderStatus; - onSetReminderClick: () => void; - onCloseReminderClick: () => void; -} - -// --- Components --- // - -export const ContributionsEpicReminderSignedIn: ReactComponent< - ContributionsEpicReminderSignedInProps -> = ({ - reminderLabel, - reminderStatus, - onSetReminderClick, - onCloseReminderClick, -}: ContributionsEpicReminderSignedInProps) => { - const reminderDateWithPreposition = ensureHasPreposition(reminderLabel); - - return ( -
-
- -
- -
- -
- - {reminderStatus === ReminderStatus.Completed ? ( - <> -

Thank you! Your reminder is set.

-

- We will be in touch to invite you to contribute. Look out for a message in - your inbox {reminderDateWithPreposition}. If you have any questions about - contributing, please{' '} - - contact us - - . -

- - ) : ( - <> -

- Show your support for the Guardian at a later date. To make this easier, set - a reminder and we’ll email you {reminderDateWithPreposition}. -

- -
-
- - - - - - - -
- - -
- - {reminderStatus === ReminderStatus.Error && ( -

- Sorry we couldn't set a reminder for you this time. Please try - again later. -

- )} - -

- We will send you a maximum of two emails {reminderDateWithPreposition}. To - find out what personal data we collect and how we use it, view our{' '} - - Privacy Policy - - . -

- - )} -
- ); -}; diff --git a/packages/modules/src/modules/epics/ContributionsEpicReminderSignedOut.stories.tsx b/packages/modules/src/modules/epics/ContributionsEpicReminderSignedOut.stories.tsx deleted file mode 100644 index 03cd6a142..000000000 --- a/packages/modules/src/modules/epics/ContributionsEpicReminderSignedOut.stories.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import React from 'react'; -import { Story, Meta } from '@storybook/react'; -import { - ContributionsEpicReminderSignedOut, - ContributionsEpicReminderSignedOutProps, -} from './ContributionsEpicReminderSignedOut'; -import { ReminderStatus } from '../utils/reminders'; -import { EpicDecorator } from './ContributionsEpic.stories'; - -export default { - component: ContributionsEpicReminderSignedOut, - title: 'Epics/ContributionsEpicReminderSignedOut', - args: { - reminderLabel: 'May', - reminderStatus: ReminderStatus.Editing, - }, - decorators: [EpicDecorator], -} as Meta; - -const Template: Story = ( - props: ContributionsEpicReminderSignedOutProps, -) => ; - -export const Default = Template.bind({}); - -export const Submitting = Template.bind({}); -Submitting.args = { - reminderStatus: ReminderStatus.Submitting, -}; - -export const Completed = Template.bind({}); -Completed.args = { - reminderStatus: ReminderStatus.Completed, -}; - -export const Error = Template.bind({}); -Error.args = { - reminderStatus: ReminderStatus.Error, -}; diff --git a/packages/modules/src/modules/epics/ContributionsEpicReminderSignedOut.tsx b/packages/modules/src/modules/epics/ContributionsEpicReminderSignedOut.tsx deleted file mode 100644 index c2e0b62d2..000000000 --- a/packages/modules/src/modules/epics/ContributionsEpicReminderSignedOut.tsx +++ /dev/null @@ -1,217 +0,0 @@ -// --- Imports --- // - -import React from 'react'; -import { css, SerializedStyles } from '@emotion/react'; -import { headline, textSans, body } from '@guardian/src-foundations/typography'; -import { palette, space } from '@guardian/src-foundations'; -import { from } from '@guardian/src-foundations/mq'; -import { Lines } from '../shared/Lines'; -import { TextInput } from '@guardian/src-text-input'; -import { Button } from '@guardian/src-button'; -import { SvgArrowRightStraight, SvgCross } from '@guardian/src-icons'; -import { ensureHasPreposition, ReminderStatus } from '../utils/reminders'; -import { useContributionsReminderEmailForm } from '../../hooks/useContributionsReminderEmailForm'; -import type { ReactComponent } from '../../types'; - -// --- Styles --- // - -const rootStyles = css` - position: relative; - - * { - box-sizing: border-box; - } -`; - -const closeButtonStyles = css` - width: 30px; - height: 30px; - cursor: pointer; - position: absolute; - top: 20px; - right: 0; - background: none; - border: none; - padding: 0; -`; - -const lineWrapperStyles = css` - margin: ${space[3]}px auto; -`; - -const containerStyles = css` - padding: 0 ${space[1]}px; -`; - -const remindHeading = css` - ${headline.xxsmall({ fontWeight: 'bold' })}; - margin: 0 ${space[5]}px ${space[2]}px 0; -`; - -const successTextStyles = css` - margin: 0 auto ${space[2]}px; - ${body.medium()}; -`; - -const linkStyles = css` - color: ${palette.neutral[7]}; -`; - -const formWrapper = css` - width: 100%; - display: flex; - flex-direction: column; - align-items: flex-start; - - ${from.tablet} { - flex-direction: row; - align-items: flex-end; - justify-content: flex-start; - } -`; - -const inputWrapper = css` - width: 100%; - margin-bottom: ${space[2]}px; - flex-grow: 1; - - ${from.tablet} { - width: auto; - margin-right: ${space[2]}px; - margin-bottom: 0; - } -`; - -const formTextStyles = css` - ${textSans.small()}; - font-style: italic; - margin-top: ${space[1]}px; -`; - -const errorTextStyles = css` - ${textSans.small({ fontWeight: 'bold' })}; - color: ${palette.error[400]}; - font-style: italic; - margin-top: ${space[1]}px; - margin-bottom: 0; -`; - -const getCustomSubmitStyles = (isDisabled: boolean): SerializedStyles | undefined => { - if (isDisabled) { - // Unfortunately these overrides need !important as otherwise they'll lose - // the specificity war against the default Source styles. - return css` - pointer-events: none; - color: ${palette.neutral[60]} !important; - background-color: ${palette.neutral[93]} !important; - border: 1px solid ${palette.neutral[86]} !important; - `; - } - - return undefined; -}; - -// --- Types --- // - -export interface ContributionsEpicReminderSignedOutProps { - reminderLabel: string; - reminderStatus: ReminderStatus; - onSetReminderClick: (email: string) => void; - onCloseReminderClick: () => void; -} - -// --- Components --- // - -export const ContributionsEpicReminderSignedOut: ReactComponent< - ContributionsEpicReminderSignedOutProps -> = ({ - reminderLabel, - reminderStatus, - onSetReminderClick, - onCloseReminderClick, -}: ContributionsEpicReminderSignedOutProps) => { - const { email, updateEmail, inputError, handleSubmit } = useContributionsReminderEmailForm(); - - const reminderDateWithPreposition = ensureHasPreposition(reminderLabel); - - return ( -
- - -
- -
- -
-
onSetReminderClick(email))}> - {reminderStatus !== ReminderStatus.Completed && ( - <> -

Remind me {reminderDateWithPreposition}

-
-
- -
- -
- - )} - - {reminderStatus === ReminderStatus.Error && ( -

- Sorry we couldn't set a reminder for you this time. Please try - again later. -

- )} -
- - {reminderStatus !== ReminderStatus.Completed && ( -

- We will send you a maximum of two emails {reminderDateWithPreposition}. To - find out what personal data we collect and how we use it, view our{' '} - - Privacy Policy - - . -

- )} - - {reminderStatus === ReminderStatus.Completed && ( - <> -

Thank you! Your reminder is set.

-

- We will be in touch to invite you to contribute. Look out for a message - in your inbox {reminderDateWithPreposition}. If you have any questions - about contributing, please{' '} - - contact us - - . -

- - )} -
-
- ); -}; diff --git a/packages/modules/src/modules/epics/ContributionsEpicSignInCta.tsx b/packages/modules/src/modules/epics/ContributionsEpicSignInCta.tsx deleted file mode 100644 index e791b534b..000000000 --- a/packages/modules/src/modules/epics/ContributionsEpicSignInCta.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import React from 'react'; -import { css } from '@emotion/react'; -import { Link } from '@guardian/src-link'; -import { body } from '@guardian/src-foundations/typography'; -import { neutral } from '@guardian/src-foundations'; -import { OphanComponentEvent } from '@sdc/shared/types'; -import { OPHAN_COMPONENT_SIGN_IN } from './utils/ophan'; -import type { ReactComponent } from '../../types'; - -const signInLink = css` - margin: 0; - border-top: 1px solid ${neutral[0]}; -`; - -const signInLinkText = css` - ${body.medium({ fontWeight: 'bold' })}; -`; - -const signInUrl = - 'https://profile.theguardian.com/signin?utm_source=gdnwb&utm_medium=banner&utm_campaign=SigninEPIC_Existing&CMP_TU=mrtn&CMP_BUNIT=subs'; - -interface ContributionsEpicSignInProps { - submitComponentEvent?: (event: OphanComponentEvent) => void; -} - -export const ContributionsEpicSignInCta: ReactComponent = ({ - submitComponentEvent, -}: ContributionsEpicSignInProps) => { - const onSignInClick = () => { - if (submitComponentEvent) { - submitComponentEvent(OPHAN_COMPONENT_SIGN_IN); - } - }; - - return ( -

- Already a supporter?{' '} - - Sign in - {' '} - and you’ll see far fewer of these messages. -

- ); -}; diff --git a/packages/modules/src/modules/epics/ContributionsEpicTicker.stories.tsx b/packages/modules/src/modules/epics/ContributionsEpicTicker.stories.tsx deleted file mode 100644 index 15310b77a..000000000 --- a/packages/modules/src/modules/epics/ContributionsEpicTicker.stories.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React from 'react'; -import { Story, Meta } from '@storybook/react'; -import { ContributionsEpicTicker, Props } from './ContributionsEpicTicker'; -import { TickerCountType, TickerEndType } from '@sdc/shared/types'; - -export default { - component: ContributionsEpicTicker, - title: 'Epics/ContributionsTicker', - args: { - settings: { - countType: TickerCountType.money, - endType: TickerEndType.unlimited, - currencySymbol: '£', - copy: { - countLabel: 'contributed', - goalReachedPrimary: "We've met our goal - thank you", - goalReachedSecondary: 'Contributions are still being accepted', - }, - }, - total: 50_000, - goal: 100_000, - }, -} as Meta; - -const Template: Story = (props: Props) => ; - -export const Default = Template.bind({}); - -export const GoalReached = Template.bind({}); -GoalReached.args = { - total: 150_000, -}; diff --git a/packages/modules/src/modules/epics/ContributionsEpicTicker.tsx b/packages/modules/src/modules/epics/ContributionsEpicTicker.tsx deleted file mode 100644 index 9555f1bf8..000000000 --- a/packages/modules/src/modules/epics/ContributionsEpicTicker.tsx +++ /dev/null @@ -1,189 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { css, SerializedStyles } from '@emotion/react'; -import { palette } from '@guardian/src-foundations'; -import { textSans } from '@guardian/src-foundations/typography'; -import { useHasBeenSeen, HasBeenSeen } from '../../hooks/useHasBeenSeen'; -import useTicker from '../../hooks/useTicker'; -import { TickerSettings } from '@sdc/shared/types'; -import type { ReactComponent } from '../../types'; - -// This ticker component provides an animated progress bar and counter for the -// epic. It mirrors the behaviour of the "unlimited" ticker type from frontend. -// The "hardstop" type is not supported. The differences between the two relate -// to behaviour once the goal has been reached. - -const rootStyles = css` - position: relative; - height: 65px; - margin-bottom: 15px; - line-height: 18px; -`; - -const totalCountStyles = css` - ${textSans.medium({ fontWeight: 'bold' })} -`; - -const soFarCountStyles = css` - ${textSans.medium({ fontWeight: 'bold' })} -`; - -const countLabelStyles = css` - ${textSans.medium()} -`; - -const progressBarHeight = 10; - -const progressBarContainerStyles = css` - width: 100%; - height: ${progressBarHeight}px; - background-color: #dda7a1; - position: absolute; - bottom: 0; - margin-top: 40px; -`; - -const progressBarStyles = css` - overflow: hidden; - width: 100%; - height: ${progressBarHeight}px; - position: absolute; -`; - -const soFarContainerStyles = css` - position: absolute; - left: 0; - bottom: ${progressBarHeight + 5}px; -`; - -const progressBarTransform = (end: number, runningTotal: number, total: number): string => { - const haveStartedAnimating = runningTotal > 0; - - if (!haveStartedAnimating) { - return 'translateX(-100%)'; - } - - const percentage = (total / end) * 100 - 100; - - return `translate3d(${percentage >= 0 ? 0 : percentage}%, 0, 0)`; -}; - -const filledProgressStyles = ( - end: number, - runningTotal: number, - total: number, -): SerializedStyles => css` - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - transform: ${progressBarTransform(end, runningTotal, total)}; - transition: transform 3s cubic-bezier(0.25, 0.55, 0.2, 0.85); - background-color: ${palette.news[400]}; -`; - -const goalContainerStyles: SerializedStyles = css` - position: absolute; - right: 0; - bottom: ${progressBarHeight + 5}px; - text-align: right; -`; - -const goalMarkerStyles = (transform: string): SerializedStyles => css` - border-right: 2px solid ${palette.neutral[7]}; - content: ' '; - display: block; - height: 12px; - margin-top: -2px; - transform: ${transform}; -`; - -type MarkerProps = { - goal: number; - end: number; -}; - -const Marker: ReactComponent = ({ goal, end }: MarkerProps) => { - if (end > goal) { - const markerTranslate = (goal / end) * 100 - 100; - const markerTransform = `translate3d(${markerTranslate}%, 0, 0)`; - - return
; - } else { - return <>; - } -}; - -export type Props = { - settings: TickerSettings; - total: number; - goal: number; -}; - -export const ContributionsEpicTicker: ReactComponent = ({ - settings, - total, - goal, -}: Props) => { - const [readyToAnimate, setReadyToAnimate] = useState(false); - - const debounce = true; - const [hasBeenSeen, setNode] = useHasBeenSeen( - { - rootMargin: '-18px', - threshold: 0, - }, - debounce, - ) as HasBeenSeen; - - useEffect(() => { - if (hasBeenSeen) { - setTimeout(() => setReadyToAnimate(true), 500); - } - }, [hasBeenSeen]); - - const runningTotal = useTicker(total, readyToAnimate); - - const goalReached = total >= goal; - const currencySymbol = settings.countType === 'money' ? settings.currencySymbol : ''; - - // If we've exceeded the goal then extend the bar 15% beyond the total - const end = total > goal ? total + total * 0.15 : goal; - - return ( -
-
-
-
- {goalReached - ? settings.copy.goalReachedPrimary - : `${currencySymbol}${runningTotal.toLocaleString()}`} -
-
- {goalReached - ? settings.copy.goalReachedSecondary - : settings.copy.countLabel} -
-
- -
-
- {goalReached - ? `${currencySymbol}${total.toLocaleString()}` - : `${currencySymbol}${goal.toLocaleString()}`} -
-
- {goalReached ? settings.copy.countLabel : 'our goal'} -
-
-
- -
-
-
-
- -
-
- ); -}; diff --git a/packages/modules/src/modules/epics/ContributionsLiveblogEpic.stories.tsx b/packages/modules/src/modules/epics/ContributionsLiveblogEpic.stories.tsx deleted file mode 100644 index 64f2fca41..000000000 --- a/packages/modules/src/modules/epics/ContributionsLiveblogEpic.stories.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import React from 'react'; -import { ContributionsLiveblogEpic } from './ContributionsLiveblogEpic'; -import { Story, Meta } from '@storybook/react'; -import { EpicProps, SecondaryCtaType } from '@sdc/shared/types'; -import { props } from './utils/storybook'; - -export default { - component: ContributionsLiveblogEpic, - title: 'Epics/ContributionsLiveblogEpic', - args: props, -} as Meta; - -const Template: Story = (props: EpicProps) => ; - -export const WhenOnDCR = Template.bind({}); -WhenOnDCR.args = { - tracking: { - ...props.tracking, - clientName: 'dcr', - }, - variant: { - ...props.variant, - secondaryCta: undefined, - }, -}; - -export const WithoutSupportUrl = Template.bind({}); -WithoutSupportUrl.args = { - variant: { - ...props.variant, - cta: { - baseUrl: 'https://theguardian.com', - text: 'The Guardian', - }, - secondaryCta: undefined, - }, -}; - -export const WithReminderCta = Template.bind({}); -WithReminderCta.args = { - variant: { - ...props.variant, - secondaryCta: { - type: SecondaryCtaType.ContributionsReminder, - }, - showReminderFields: { - reminderCta: 'Remind me in December', - reminderPeriod: '2022-12-01', - reminderLabel: 'December', - }, - }, -}; - -export const WithBespokeCta = Template.bind({}); diff --git a/packages/modules/src/modules/epics/ContributionsLiveblogEpic.testData.ts b/packages/modules/src/modules/epics/ContributionsLiveblogEpic.testData.ts deleted file mode 100644 index cfaf87e89..000000000 --- a/packages/modules/src/modules/epics/ContributionsLiveblogEpic.testData.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { EpicTargeting, PageTracking, TestTracking, Tracking } from '@sdc/shared/types'; - -const content = { - paragraphs: [ - 'In these extraordinary times, the Guardian’s editorial independence has never been more important. Because no one sets our agenda, or edits our editor, we can keep delivering quality, trustworthy, fact-checked journalism each and every day. Free from commercial or political bias, we can report fearlessly on world events and challenge those in power.', - 'Your support protects the Guardian’s independence. We believe every one of us deserves equal access to accurate news and calm explanation. No matter how unpredictable the future feels, we will remain with you, delivering high quality news so we can all make critical decisions about our lives, health and security – based on fact, not fiction.', - 'Support the Guardian from as little as £1 – and it only takes a minute. Thank you.', - ], - cta: { - text: 'Make a contribution', - baseUrl: 'https://support.theguardian.com/contribute', - }, -}; - -const pageTracking: PageTracking = { - ophanPageId: 'k5nxn0mxg7ytwpkxuwms', - platformId: 'GUARDIAN_WEB', - clientName: 'dcr', - referrerUrl: - 'http://localhost:3000/politics/2020/jan/17/uk-rules-out-automatic-deportation-of-eu-citizens-verhofstadt-brexit', -}; - -const testTracking: TestTracking = { - campaignCode: 'gdnwb_copts_memco_remote_epic_test_api', - campaignId: 'remote_epic_test', - abTestName: 'remote_epic_test', - abTestVariant: 'api', - componentType: 'ACQUISITIONS_EPIC', - products: ['CONTRIBUTION', 'MEMBERSHIP_SUPPORTER'], -}; - -const tracking: Tracking = { - ...pageTracking, - ...testTracking, -}; - -const targeting: EpicTargeting = { - contentType: 'Article', - sectionId: 'environment', - shouldHideReaderRevenue: false, - isMinuteArticle: false, - isPaidContent: false, - tags: [ - { - id: 'environment/drought', - type: 'Keyword', - }, - { - id: 'environment/climate-change', - type: 'Keyword', - }, - ], - showSupportMessaging: true, - isRecurringContributor: false, - lastOneOffContributionDate: 1548979200000, // 2019-02-01 - mvtId: 2, - weeklyArticleHistory: [ - { week: 18337, count: 10 }, - { week: 18330, count: 5 }, - ], - hasOptedOutOfArticleCount: false, - countryCode: 'GB', -}; - -const testData = { content, tracking, testTracking, pageTracking, targeting }; - -export default testData; diff --git a/packages/modules/src/modules/epics/ContributionsLiveblogEpic.tsx b/packages/modules/src/modules/epics/ContributionsLiveblogEpic.tsx deleted file mode 100644 index 0ddfef7d6..000000000 --- a/packages/modules/src/modules/epics/ContributionsLiveblogEpic.tsx +++ /dev/null @@ -1,182 +0,0 @@ -import React, { useEffect } from 'react'; -import { css } from '@emotion/react'; -import { body, headline } from '@guardian/src-foundations/typography'; -import { from } from '@guardian/src-foundations/mq'; -import { palette } from '@guardian/src-foundations'; -import { neutral, brandAlt } from '@guardian/src-foundations/palette'; -import { space } from '@guardian/src-foundations'; -import { - replaceNonArticleCountPlaceholders, - containsNonArticleCountPlaceholder, - createViewEventFromTracking, - createInsertEventFromTracking, -} from '@sdc/shared/lib'; -import { EpicProps } from '@sdc/shared/types'; -import { replaceArticleCount } from '../../lib/replaceArticleCount'; -import { HasBeenSeen, useHasBeenSeen } from '../../hooks/useHasBeenSeen'; -import { logEpicView } from '@sdc/shared/lib'; -import { ContributionsEpicCtas } from './ContributionsEpicCtas'; -import type { ReactComponent } from '../../types'; - -const container = (clientName: string) => css` - padding: 6px 10px 28px 10px; - border-top: 1px solid ${brandAlt[400]}; - border-bottom: 1px solid ${neutral[86]}; - background: ${neutral[100]}; - - border: 1px solid ${neutral[0]}; - - * { - ::selection { - background: ${palette.brandAlt[400]}; - } - } - - & > * + * { - margin-top: ${space[3]}px; - } - - ${from.tablet} { - padding-left: ${clientName === 'dcr' ? '60px' : '80px'}; - padding-right: 20px; - - & > * + * { - margin-top: ${space[4]}px; - } - } -`; - -const textContainer = css` - ${body.medium()}; - font-size: 16px; - - p { - margin: 0; - } - - & > p + p { - margin-top: ${space[3]}px; - } - - ${from.tablet} { - & > p + p { - margin-top: ${space[4]}px; - } - } -`; - -const yellowHeading = (clientName: string) => css` - ${headline.medium({ fontWeight: 'bold' })}; - font-size: 28px; - background-color: ${brandAlt[400]}; - border-top: 1px solid ${neutral[0]}; - border-left: 1px solid ${neutral[0]}; - border-right: 1px solid ${neutral[0]}; - - padding: 8px 10px 12px 10px; - ${from.tablet} { - padding-left: ${clientName === 'dcr' ? '60px' : '80px'}; - padding-right: 20px; - } -`; - -interface LiveblogEpicBodyParagraphProps { - paragraph: string; - numArticles: number; -} - -const LiveblogEpicBodyParagraph: ReactComponent = ({ - paragraph, - numArticles, -}: LiveblogEpicBodyParagraphProps) => { - const elements = replaceArticleCount(paragraph, numArticles, 'epic'); - - return

{elements}

; -}; - -interface LiveblogEpicBodyProps { - paragraphs: string[]; - numArticles: number; -} - -const LiveblogEpicBody: ReactComponent = ({ - numArticles, - paragraphs, -}: LiveblogEpicBodyProps) => { - return ( -
- {paragraphs.map((paragraph) => ( - - ))} -
- ); -}; - -export const ContributionsLiveblogEpic: ReactComponent = ({ - variant, - countryCode, - articleCounts, - tracking, - submitComponentEvent, - onReminderOpen, - fetchEmail, -}: EpicProps): JSX.Element => { - const [hasBeenSeen, setNode] = useHasBeenSeen({ threshold: 0 }, true) as HasBeenSeen; - - useEffect(() => { - if (hasBeenSeen) { - // For epic view count - logEpicView(tracking.abTestName); - - // For ophan - if (submitComponentEvent) { - submitComponentEvent(createViewEventFromTracking(tracking, tracking.campaignCode)); - } - } - }, [hasBeenSeen, submitComponentEvent]); - - useEffect(() => { - if (submitComponentEvent) { - submitComponentEvent(createInsertEventFromTracking(tracking, tracking.campaignCode)); - } - }, [submitComponentEvent]); - - const cleanParagraphs = variant.paragraphs.map((paragraph) => - replaceNonArticleCountPlaceholders(paragraph, countryCode), - ); - const cleanHeading = - replaceNonArticleCountPlaceholders(variant.heading) || 'Support the Guardian'; - - if ( - cleanParagraphs.some(containsNonArticleCountPlaceholder) || - containsNonArticleCountPlaceholder(cleanHeading) - ) { - return <>; - } - - return ( -
- {cleanHeading &&
{cleanHeading}
} -
- - - -
-
- ); -}; diff --git a/packages/modules/src/modules/epics/NewsletterSignup.tsx b/packages/modules/src/modules/epics/NewsletterSignup.tsx deleted file mode 100644 index 4fe6114b4..000000000 --- a/packages/modules/src/modules/epics/NewsletterSignup.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import React, { useEffect, useRef, useState } from 'react'; -import { css } from '@emotion/react'; -import { space } from '@guardian/src-foundations'; - -const containerStyles = css` - margin: ${space[6]}px ${space[2]}px ${space[1]}px 0; - width: 100%; -`; - -const NewsletterSignup = ({ url }: { url: string }): JSX.Element => { - const [iframeHeight, setIframeHeight] = useState(60); - const iframeRef = useRef(null); - - useEffect(() => { - // Handle iframe resize events. Based on https://github.com/guardian/dotcom-rendering/blob/main/dotcom-rendering/src/web/browser/newsletterEmbedIframe/init.ts - window.addEventListener('message', (event) => { - try { - // Check if this is the newsletter iframe - const contentWindow = iframeRef?.current?.contentWindow; - if (contentWindow && event.source && contentWindow === event.source) { - const message = JSON.parse(event.data); - - if (message.type === 'set-height') { - if (typeof message.value === 'number') { - setIframeHeight(message.value); - } else if (typeof message.value === 'string') { - const value = parseInt(message.value, 10); - if (Number.isInteger(value)) { - setIframeHeight(message.value); - } - } - } - } - } catch (err) { - console.log(`Error handling event in epic NewsletterSignup: ${err}`); - } - }); - }, []); - - return ( -
-