From b3bb9e78270129a3fd29dbac9111d12602309696 Mon Sep 17 00:00:00 2001 From: Ahmad Abdolsaheb Date: Wed, 13 Dec 2023 12:18:35 +0300 Subject: [PATCH] feat(client): update donate page UI (#52524) Co-authored-by: Shaun Hamilton Co-authored-by: Huyen Nguyen <25715018+huyenltnguyen@users.noreply.github.com> --- client/i18n/locales/english/translations.json | 26 +++- .../Donation/donation-text-components.tsx | 126 +++++++++++++++-- client/src/components/Donation/donation.css | 105 +++++++++----- client/src/pages/donate.tsx | 129 +++++++++++------- .../default/learn/donate/donate-page-donor.ts | 13 +- e2e/donate-page-default.spec.ts | 13 -- tools/ui-components/src/col/col.tsx | 3 +- tools/ui-components/src/col/types.ts | 2 +- 8 files changed, 301 insertions(+), 116 deletions(-) diff --git a/client/i18n/locales/english/translations.json b/client/i18n/locales/english/translations.json index f11602d4256..e19ac2581cd 100644 --- a/client/i18n/locales/english/translations.json +++ b/client/i18n/locales/english/translations.json @@ -472,7 +472,7 @@ "thank-you": "Thank you for being a supporter.", "success-card-update": "Your card has been updated successfully.", "additional": "You can make an additional one-time donation of any amount using this link: <0>{{url}}", - "help-more": "Help us do more", + "help-more": "Help Our Charity Do More", "error": "Something went wrong with your donation.", "error-card-update": "Something went wrong with updating your card.", "error-2": "Something is not right. Please contact donors@freecodecamp.org", @@ -528,7 +528,7 @@ "card-number": "Your Card Number:", "expiration": "Expiration Date:", "secure-donation": "Secure donation", - "faq": "Frequently asked questions", + "faq": "Frequently asked questions:", "only-you": "Only you can see this message. Congratulations on earning this certification. It's no easy task. Running freeCodeCamp isn't easy either. Nor is it cheap. Help us help you and many other people around the world. Make a tax-deductible supporting donation to our charity today.", "get-help": "How can I get help with my donations?", "how-transparent": "How transparent is freeCodeCamp.org?", @@ -565,7 +565,27 @@ "anything-else": "Is there anything else I can learn about donating to freeCodeCamp.org?", "other-support": "If there is some other way you'd like to support our charity and its mission that isn't listed here, or if you have any questions at all, please email Quincy at quincy@freecodecamp.org.", "bear-progress-alt": "Illustration of an adorable teddy bear with a pleading expression holding an empty money jar.", - "bear-completion-alt": "Illustration of an adorable teddy bear holding a large trophy." + "bear-completion-alt": "Illustration of an adorable teddy bear holding a large trophy.", + "crucial-contribution": "Your contribution will be crucial in creating resources that empower millions of people to learn new skills and support their families.", + "if-another-monthly": "If you want to make another monthly donation, please proceed with selecting your monthly donation amount.", + "support-benefits-title": "Benefits from becoming a Supporter:", + "support-benefits-1": "No more donation prompt popups", + "support-benefits-2": "You'll get a Supporter badge", + "support-benefits-3": "Your profile image will get a golden halo around it", + "support-benefits-4": "You'll gain access to special Supporter Discord channels", + "support-benefits-5": "And more benefits to come in 2024", + "current-initiatives-title": "Current Initiatives:", + "current-initiatives-1": "Creating new JavaScript and Python curricula", + "current-initiatives-2": "Creating English and math curricula", + "current-initiatives-3": "Translating our curriculum and tutorials into 32 languages", + "current-initiatives-4": "Creating a free accredited computer science bachelor's degree", + "community-achivements-title": "Our Community Achievements This Year:", + "community-achivements-1": "Published <0>114 full-length courses on YouTube.", + "community-achivements-2": "Published <0>1,045 text-based coding tutorials and <0>20 free books through freeCodeCamp Press.", + "community-achivements-3": "Merged <0>2,753 code contributions into our open source repositories on GitHub", + "community-achivements-4": "Translated <0>2,106,203 words to make our curriculum and tutorials more accessible to speakers of many world languages", + "as-you-see": "As you can see, we're getting things done. So you can rest assured that we'll put your donations to good use.", + "get-benefits": "Get the benefits and the knowledge that you’re helping our charity change education for the better. Become a Supporter today." }, "report": { "sign-in": "You need to be signed in to report a user", diff --git a/client/src/components/Donation/donation-text-components.tsx b/client/src/components/Donation/donation-text-components.tsx index ecc6132d538..17c9992eedc 100644 --- a/client/src/components/Donation/donation-text-components.tsx +++ b/client/src/components/Donation/donation-text-components.tsx @@ -1,6 +1,7 @@ import React, { useState } from 'react'; import { useTranslation, Trans } from 'react-i18next'; import Caret from '../../assets/icons/caret'; +import { Spacer } from '../helpers'; const POBOX = ( <> @@ -14,10 +15,12 @@ const POBOX = ( ); -export const DonationText = (): JSX.Element => { +export const CtaText = (): JSX.Element => { const { t } = useTranslation(); return ( <> +

{t('donate.help-more')}

+

{t('donate.efficiency')}

{t('donate.why-donate-1')} @@ -29,17 +32,37 @@ export const DonationText = (): JSX.Element => { ); }; -export const DonationOptionsAlertText = (): JSX.Element => { +export const ThankYouMessage = ({ + askForDonation +}: { + askForDonation: boolean; +}): JSX.Element => { const { t } = useTranslation(); return ( -

- donate.bigger-donation{' '} - - - placeholder - - -

+ <> +

+ {t('donate.thank-you')} +

+ {askForDonation && ( + <> + +

+ {t('donate.crucial-contribution')} +

+

+ {t('donate.bigger-donation')}{' '} + + + placeholder + + +

+

+ {t('donate.if-another-monthly')} +

+ + )} + ); }; @@ -167,6 +190,87 @@ export const DonationFaqText = (): JSX.Element => { ]; return ( - <>{faqItems.map((item, iterator) => FaqItem(item.Q, item.A, iterator))} + <> +

{t('donate.faq')}

+ + {faqItems.map((item, iterator) => FaqItem(item.Q, item.A, iterator))} + + ); +}; + +export const SupportBenefitsText = (): JSX.Element => { + const { t } = useTranslation(); + return ( + <> +

{t('donate.support-benefits-title')}

+
    +
  • {t('donate.support-benefits-1')}
  • +
  • {t('donate.support-benefits-2')}
  • +
  • {t('donate.support-benefits-3')}
  • +
  • {t('donate.support-benefits-4')}
  • +
  • {t('donate.support-benefits-5')}
  • +
+ + ); +}; + +export const CurrentInitiativesText = (): JSX.Element => { + const { t } = useTranslation(); + return ( + <> +

{t('donate.current-initiatives-title')}

+
    +
  • {t('donate.current-initiatives-1')}
  • +
  • {t('donate.current-initiatives-2')}
  • +
  • {t('donate.current-initiatives-3')}
  • +
  • {t('donate.current-initiatives-4')}
  • +
+ + ); +}; + +export const CommunityAchievementsText = (): JSX.Element => { + const { t } = useTranslation(); + return ( + <> +

{t('donate.community-achivements-title')}

+
    +
  • + + placeholder + +
  • +
  • + + placeholder + +
  • +
  • + + placeholder + +
  • +
  • + + placeholder + +
  • +
+ + ); +}; + +export const GetSupporterBenefitsText = ({ + isDonating +}: { + isDonating: boolean; +}): JSX.Element => { + const { t } = useTranslation(); + return ( + <> + +

{t('donate.as-you-see')}

+ {!isDonating ?

{t('donate.get-benefits')}

: null} + ); }; diff --git a/client/src/components/Donation/donation.css b/client/src/components/Donation/donation.css index e01f64bcefe..d0c7e221823 100644 --- a/client/src/components/Donation/donation.css +++ b/client/src/components/Donation/donation.css @@ -88,19 +88,6 @@ margin-bottom: 8px; } -.donate-page-wrapper .paypal-buttons-container { - margin-bottom: 0; -} - -@media (min-width: 1200px) { - /* - This style allows multi columns in the same row. - It will become a functionality once the Grid system is updated. - */ - .donate-page-wrapper > div > div { - float: left; - } -} .donate-input-element { padding-top: 8px; } @@ -243,12 +230,6 @@ li.disabled > a { color: var(--gray-15) !important; } -.donate-page-wrapper h2, -.donate-page-wrapper h3, -[name='payment-method'] { - font-family: var(--font-family-sans-serif); -} - @media (max-width: 400px) { .donate-tabs > .nav-pills > li > a { font-size: 0.8rem; @@ -403,10 +384,6 @@ li.disabled > a { margin-top: 0; height: 39px; } - .donate-page-wrapper .form-payment-methods { - height: 22px; - width: 220px; - } .donation-icon-container { margin: 40px; } @@ -417,13 +394,6 @@ li.disabled > a { } } -@media screen and (min-width: 1200px) { - .donate-page-wrapper .form-payment-methods { - height: 25px; - width: 250px; - } -} - .paypal-button-onetime { border: 1px solid #ffc439; padding: 2px; @@ -584,11 +554,6 @@ a.patreon-button:hover { font-weight: bold; } -.donate-page-wrapper [role='alert'] a:hover { - color: #327290; - background-color: #acdef3; -} - .separator { display: flex; align-items: center; @@ -670,3 +635,73 @@ a.patreon-button:hover { background-color: var(--blue70); border-color: var(--blue70); } + +/* donation page */ + +.donate-page-container h1, +.donate-page-container h2, +.donate-page-container h3 { + font-family: var(--font-family-sans-serif); +} + +.donate-page-container h1 { + font-size: 2.5rem; +} + +.donate-page-container .donation-section > div { + margin-top: 40px; + margin-bottom: 40px; +} + +.donate-page-container .donation-section { + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + min-height: 60vh; +} + +.donate-page-container .paypal-buttons-container { + margin-bottom: 0; +} + +.dark-palette .gradient-container { + background: linear-gradient( + -10deg, + rgb(7 40 94) 35%, + rgba(237, 202, 216, 0) 75%, + rgb(127 30 52) 100% + ), + radial-gradient(circle, rgb(112 0 48) 0%, rgb(0 63 136) 100%); +} + +.light-palette .gradient-container { + background: linear-gradient( + -10deg, + rgb(223 243 255) 35%, + rgba(237, 202, 216, 0) 75%, + rgb(255 215 224) 100% + ), + radial-gradient( + circle, + rgba(255, 202, 225, 1) 0%, + rgba(218, 234, 252, 1) 100% + ); +} + +@media screen and (min-width: 991px) { + .donate-page-container .form-payment-methods { + height: 22px; + width: 220px; + } +} + +@media screen and (min-width: 1200px) { + .donate-page-container .form-payment-methods { + height: 25px; + width: 250px; + } + .donate-page-container .donation-section { + flex-direction: row; + } +} diff --git a/client/src/pages/donate.tsx b/client/src/pages/donate.tsx index 9d700231d49..17104859f0a 100644 --- a/client/src/pages/donate.tsx +++ b/client/src/pages/donate.tsx @@ -1,4 +1,4 @@ -import { Alert, Container, Col, Row } from '@freecodecamp/ui'; +import { Container, Col, Row } from '@freecodecamp/ui'; import type { TFunction } from 'i18next'; import React, { useEffect } from 'react'; import Helmet from 'react-helmet'; @@ -10,15 +10,23 @@ import { createSelector } from 'reselect'; import MultiTierDonationForm from '../components/Donation/multi-tier-donation-form'; import { - DonationText, - DonationOptionsAlertText, - DonationFaqText + CtaText, + ThankYouMessage, + DonationFaqText, + SupportBenefitsText, + CurrentInitiativesText, + CommunityAchievementsText, + GetSupporterBenefitsText } from '../components/Donation/donation-text-components'; import { Spacer, Loader } from '../components/helpers'; -import CampersImage from '../components/landing/components/campers-image'; import { executeGA } from '../redux/actions'; -import { signInLoadingSelector, userSelector } from '../redux/selectors'; +import { + signInLoadingSelector, + userSelector, + donationFormStateSelector +} from '../redux/selectors'; import { PaymentContext } from '../../../shared/config/donation-settings'; +import { DonateFormState } from '../redux/types'; export interface ExecuteGaArg { event: string; @@ -31,14 +39,21 @@ interface DonatePageProps { isDonating?: boolean; showLoading: boolean; t: TFunction; + donationFormState: DonateFormState; } const mapStateToProps = createSelector( userSelector, signInLoadingSelector, - ({ isDonating }: { isDonating: boolean }, showLoading: boolean) => ({ + donationFormStateSelector, + ( + { isDonating }: { isDonating: boolean }, + showLoading: boolean, + donationFormState: DonateFormState + ) => ({ isDonating, - showLoading + showLoading, + donationFormState }) ); @@ -50,7 +65,8 @@ function DonatePage({ executeGA = () => {}, isDonating = false, showLoading, - t + t, + donationFormState }: DonatePageProps) { useEffect(() => { executeGA({ @@ -65,48 +81,65 @@ function DonatePage({ ) : ( <> - - + + + + + {isDonating ? ( + + ) : ( + + )} + + + + + + + + - - {isDonating ? ( -

- {t('donate.thank-you')} -

- ) : ( -

- {t('donate.help-more')} -

- )} - - {isDonating ? ( - -

{t('donate.thank-you')}

-
- -
- ) : null} - - - -
-

- {t('donate.faq')} -

- - - - - + + +
- + + + + + + + + + + + + + + + + + +
+ + + + +
+ + +
+
+ + + + + + + ); diff --git a/cypress/e2e/default/learn/donate/donate-page-donor.ts b/cypress/e2e/default/learn/donate/donate-page-donor.ts index c241540c129..25cd8494b38 100644 --- a/cypress/e2e/default/learn/donate/donate-page-donor.ts +++ b/cypress/e2e/default/learn/donate/donate-page-donor.ts @@ -4,15 +4,16 @@ describe('Donate page', () => { cy.login(); }); - it('Donor alert should be visible for donor', () => { + it('Donor CTA should be visible for donor', () => { cy.visit('/donate'); - - cy.get('[data-cy="donate-alert"]').should('be.visible'); - cy.get('[data-cy="donate.thank-you"]').should( 'have.text', 'Thank you for being a supporter.' ); + cy.get('[data-cy="donate.crucial-contribution"]').should( + 'have.text', + 'Your contribution will be crucial in creating resources that empower millions of people to learn new skills and support their families.' + ); cy.get('[data-cy="donate.bigger-donation"]').should( 'have.text', "Want to make a bigger one-time donation, mail us a check, or give in other ways? Here are many other ways you can support our charity's mission." @@ -22,5 +23,9 @@ describe('Donate page', () => { 'href', 'https://www.freecodecamp.org/news/how-to-donate-to-free-code-camp' ); + cy.get('[data-cy="donate.make-another-monthly"]').should( + 'have.text', + 'If you want to make another monthly donation, please proceed with selecting your monthly donation amount.' + ); }); }); diff --git a/e2e/donate-page-default.spec.ts b/e2e/donate-page-default.spec.ts index d96ff309b67..7efa2b9f411 100644 --- a/e2e/donate-page-default.spec.ts +++ b/e2e/donate-page-default.spec.ts @@ -149,19 +149,6 @@ test.describe('Donate Page', () => { await expect(donateText3).toHaveText(translations.donate['why-donate-2']); }); - test('should render the campers image in desktop browsers', async ({ - isMobile - }) => { - const figure = page.getByTestId(pageElements.campersImage); - - if (isMobile) { - await expect(figure).not.toBeVisible(); - return; - } - - await expect(figure).toBeVisible(); - }); - test('should display the faq heading', async () => { const faqHead = page.getByTestId(pageElements.faqHeading); await expect(faqHead).toHaveText(translations.donate.faq); diff --git a/tools/ui-components/src/col/col.tsx b/tools/ui-components/src/col/col.tsx index 68654318cb5..cd26f72ed4a 100644 --- a/tools/ui-components/src/col/col.tsx +++ b/tools/ui-components/src/col/col.tsx @@ -36,7 +36,8 @@ const MediumClasses = { 4: 'min-[992px]:w-1/3', 6: 'min-[992px]:w-1/2', 8: 'min-[992px]:w-2/3', - 10: 'min-[992px]:w-5/6' + 10: 'min-[992px]:w-5/6', + 12: 'min-[992px]:w-full' }; const MediumOffsetClasses = { diff --git a/tools/ui-components/src/col/types.ts b/tools/ui-components/src/col/types.ts index 1db6d4bd4b9..90b5c0e77a7 100644 --- a/tools/ui-components/src/col/types.ts +++ b/tools/ui-components/src/col/types.ts @@ -3,7 +3,7 @@ export interface ColProps extends React.HTMLAttributes { children?: React.ReactNode; xs?: 6 | 8 | 10 | 12; sm?: 2 | 4 | 5 | 6 | 8 | 10 | 12; - md?: 4 | 6 | 8 | 10; + md?: 4 | 6 | 8 | 10 | 12; lg?: 6 | 8 | 10; xsOffset?: 1 | 2 | 3; smOffset?: 1 | 2 | 3 | 4;