mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-05-28 18:26:54 +00:00
fix(client): notes in superblocks intro (#67075)
Co-authored-by: Huyen Nguyen <25715018+huyenltnguyen@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
13370827c3
commit
68de9032ae
@@ -3831,6 +3831,7 @@
|
||||
"In the second half, you'll practice vocabulary specific to software development. You'll learn how to describe code, discuss tech trends, and participate in stand-up meetings.",
|
||||
"This entire A2-level curriculum includes 105 different dialogues. Each is designed to build your vocabulary and boost your confidence when speaking in a professional tech setting."
|
||||
],
|
||||
"note": "This certification is currently in beta.",
|
||||
"blocks": {
|
||||
"learn-greetings-in-your-first-day-at-the-office": {
|
||||
"title": "Learn Greetings in your First Day at the Office",
|
||||
@@ -4118,6 +4119,7 @@
|
||||
"You'll also focus on applying these skills in professional and technical settings. You'll practice vocabulary and phrases essential for developers, such as describing code, participating in stand-up meetings, and discussing tech trends. Advanced topics include conditionals, comparative structures, and conversation management, so you can prepare for real-world interactions in the tech industry.",
|
||||
"This entire B1-level curriculum includes 73 different dialogues. Each is designed to build your vocabulary and boost your confidence when speaking in a professional tech setting."
|
||||
],
|
||||
"note": "This certification is currently in beta.",
|
||||
"blocks": {
|
||||
"learn-how-to-describe-places-and-events": {
|
||||
"title": "Learn How to Describe Places and Events",
|
||||
@@ -5900,6 +5902,7 @@
|
||||
"- Complete the five required projects to qualify for the certification exam.",
|
||||
"- Pass the Front-End Development Libraries Certification exam."
|
||||
],
|
||||
"note": "",
|
||||
"chapters": {
|
||||
"front-end-development-libraries": "Front-End Development Libraries",
|
||||
"front-end-development-libraries-certification-exam": "Front-End Development Libraries Certification Exam"
|
||||
@@ -7047,6 +7050,7 @@
|
||||
"- Complete the five required projects to qualify for the certification exam.",
|
||||
"- Pass the Back-End Development and APIs Certification exam."
|
||||
],
|
||||
"note": "",
|
||||
"chapters": {
|
||||
"back-end-development-and-apis": "Back-End Development and APIs",
|
||||
"back-end-development-and-apis-certification-exam": "Back-End Development and APIs Certification Exam"
|
||||
@@ -7259,6 +7263,7 @@
|
||||
"This certification represents the culmination of your full-stack developer journey. It demonstrates your ability to build complete, modern web applications from start to finish.",
|
||||
"To qualify for the exam, you must earn the certifications below. Pass the exam to earn your Full-Stack Developer Certification."
|
||||
],
|
||||
"note": "",
|
||||
"chapters": {
|
||||
"certified-full-stack-developer-exam": "Certified Full-Stack Developer Exam"
|
||||
},
|
||||
@@ -7353,7 +7358,7 @@
|
||||
},
|
||||
"a1-professional-spanish": {
|
||||
"title": "A1 Professional Spanish Certification (Beta)",
|
||||
"note": "This certification is currently in active development. We are currently publishing the first three chapters, and future chapters will be released as they are developed by our instructional design team. Once all the chapters are available, we will release the certification exam.",
|
||||
"note": "This certification is currently in active development. New content will be published as our instructional design team develops it. Once all content is available, we will release the certification exam.",
|
||||
"intro": [
|
||||
"This course teaches you the fundamentals of Spanish at the A1 level of the Common European Framework of Reference (CEFR), with lessons focused on professional settings. Each module is broken down into sections:",
|
||||
"- A Warm-up section for quick review.",
|
||||
@@ -8666,7 +8671,7 @@
|
||||
},
|
||||
"a2-professional-spanish": {
|
||||
"title": "A2 Professional Spanish Certification (Beta)",
|
||||
"note": "This certification is currently in active development. While there isn't a claimable certification available for this section at the moment, one will be available soon. In the meantime, you're welcome to explore the courses we have created below.",
|
||||
"note": "This certification is currently in active development. New content will be published as our instructional design team develops it. Once all content is available, we will release the certification exam.",
|
||||
"intro": ["Placeholder intro"],
|
||||
"blocks": {
|
||||
"talk-about-who-you-are-by-using-key-verbs": {
|
||||
@@ -8685,7 +8690,7 @@
|
||||
},
|
||||
"a2-professional-chinese": {
|
||||
"title": "A2 Professional Chinese Certification (Beta)",
|
||||
"note": "This certification is currently in active development. While there isn't a claimable certification available for this section at the moment, one will be available soon. In the meantime, you're welcome to explore the courses we have created below.",
|
||||
"note": "This certification is currently in active development. New content will be published as our instructional design team develops it. Once all content is available, we will release the certification exam.",
|
||||
"intro": ["Placeholder intro"],
|
||||
"blocks": {
|
||||
"talk-about-what-you-do-by-using-key-verbs": {
|
||||
@@ -8704,7 +8709,7 @@
|
||||
},
|
||||
"a1-professional-chinese": {
|
||||
"title": "A1 Professional Chinese Certification (Beta)",
|
||||
"note": "This certification is in active development. We are currently publishing the introductory chapters, and future chapters will be released as they are developed by our instructional design team. Once all the chapters are available, we will release the certification exam.",
|
||||
"note": "This certification is currently in active development. New content will be published as our instructional design team develops it. Once all content is available, we will release the certification exam.",
|
||||
"intro": [
|
||||
"In this A1 Professional Chinese Curriculum, you'll learn the building blocks of the Chinese language. This will follow the A1 level of the Common European Framework of Reference (CEFR). And we've focused on vocabulary that is particularly useful for professional settings.",
|
||||
"The curriculum is broken down into several modules that include warm-up, learning, practice, review pages, and quizzes to make sure that you truly understand the material before moving on to the next module.",
|
||||
|
||||
@@ -805,7 +805,7 @@
|
||||
"unfinished-certification": "This certification is currently in active development. While there isn't a claimable certification available at the moment, one will be available soon. In the meantime, you're welcome to explore the courses we have created below.",
|
||||
"consider-donating": "Please consider donating to support the completion of its development.",
|
||||
"unfinished-certification-2": "This certification will take you a substantial amount of time and effort to complete. If you start now, you may be ready to take the final exam when we launch it in the coming months.",
|
||||
"consider-donating-2": "If you want to help us speed up development of this curriculum, please <0>consider becoming a supporter of our charity.</0>",
|
||||
"consider-donating-2": "If you want to help us speed up development of this curriculum, please consider becoming a supporter of our charity.",
|
||||
"help-us-develop": "Help us develop free professional programming certifications for all.",
|
||||
"nicely-done": "Nicely done. You just completed {{block}}.",
|
||||
"credit-card": "Credit Card",
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
.super-block-intro-callout hr {
|
||||
border-top-color: var(--blue10);
|
||||
}
|
||||
|
||||
.super-block-intro-callout .donate-button {
|
||||
background-color: transparent;
|
||||
color: #31708f;
|
||||
border-color: #31708f;
|
||||
}
|
||||
|
||||
.super-block-intro-callout .btn-container {
|
||||
text-align: center;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { useTranslation, Trans } from 'react-i18next';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import { Callout, Spacer, Container, Row, Col } from '@freecodecamp/ui';
|
||||
import {
|
||||
archivedSuperBlocks,
|
||||
@@ -12,28 +12,37 @@ import DumbbellIcon from '../../../assets/icons/dumbbell';
|
||||
import CommunityIcon from '../../../assets/icons/community';
|
||||
import ArchivedWarning from '../../../components/archived-warning';
|
||||
|
||||
import './super-block-intro.css';
|
||||
|
||||
interface ConditionalDonationAlertProps {
|
||||
superBlock: SuperBlocks;
|
||||
superBlockNoteText: string;
|
||||
onCertificationDonationAlertClick: () => void;
|
||||
isDonating: boolean;
|
||||
}
|
||||
|
||||
interface SuperBlockIntroProps extends ConditionalDonationAlertProps {
|
||||
interface SuperBlockIntroProps {
|
||||
superBlock: SuperBlocks;
|
||||
onCertificationDonationAlertClick: () => void;
|
||||
isDonating: boolean;
|
||||
hasNotstarted: boolean;
|
||||
nextChallengeSlug: string | null;
|
||||
}
|
||||
|
||||
export const ConditionalDonationAlert = ({
|
||||
superBlock,
|
||||
superBlockNoteText,
|
||||
onCertificationDonationAlertClick,
|
||||
isDonating
|
||||
}: ConditionalDonationAlertProps): JSX.Element | null => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const betaCertifications: SuperBlocks[] = [SuperBlocks.A2English];
|
||||
const betaCertifications: SuperBlocks[] = [
|
||||
SuperBlocks.A2English,
|
||||
SuperBlocks.B1English
|
||||
];
|
||||
|
||||
const unfinishedCertifications = [
|
||||
SuperBlocks.B1English,
|
||||
const unfinishedCertifications: SuperBlocks[] = [
|
||||
SuperBlocks.A1Spanish,
|
||||
SuperBlocks.A2Spanish,
|
||||
SuperBlocks.A2Chinese,
|
||||
@@ -43,14 +52,36 @@ export const ConditionalDonationAlert = ({
|
||||
SuperBlocks.FullStackDeveloperV9
|
||||
];
|
||||
|
||||
if (!isDonating && betaCertifications.includes(superBlock))
|
||||
const isBetaCertification = betaCertifications.includes(superBlock);
|
||||
const isUnfinishedCertification =
|
||||
unfinishedCertifications.includes(superBlock);
|
||||
const showDonationCopy =
|
||||
!isDonating && (isBetaCertification || isUnfinishedCertification);
|
||||
|
||||
if (!superBlockNoteText && !showDonationCopy) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Spacer size='m' />
|
||||
<Callout
|
||||
variant='note'
|
||||
label={t('misc.note')}
|
||||
className='annual-donation-alert'
|
||||
className='super-block-intro-callout'
|
||||
>
|
||||
<p>{t('donate.beta-certification')}</p>
|
||||
{superBlockNoteText && <p>{superBlockNoteText}</p>}
|
||||
{showDonationCopy && (
|
||||
<>
|
||||
<p>
|
||||
{isBetaCertification ? (
|
||||
t('donate.consider-donating')
|
||||
) : (
|
||||
<Trans i18nKey='donate.consider-donating-2'>
|
||||
<Link className='inline' to='/donate'>
|
||||
placeholder
|
||||
</Link>
|
||||
</Trans>
|
||||
)}
|
||||
</p>
|
||||
<hr />
|
||||
<p className='btn-container'>
|
||||
<Link
|
||||
@@ -63,27 +94,11 @@ export const ConditionalDonationAlert = ({
|
||||
{t('buttons.donate-now')}
|
||||
</Link>
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
</Callout>
|
||||
</>
|
||||
);
|
||||
|
||||
if (!isDonating && unfinishedCertifications.includes(superBlock))
|
||||
return (
|
||||
<Callout
|
||||
variant='note'
|
||||
label={t('misc.note')}
|
||||
className='annual-donation-alert'
|
||||
>
|
||||
<p>
|
||||
<Trans i18nKey='donate.consider-donating-2'>
|
||||
<Link className='inline' to='/donate'>
|
||||
placeholder
|
||||
</Link>
|
||||
</Trans>
|
||||
</p>
|
||||
</Callout>
|
||||
);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
function SuperBlockIntro({
|
||||
@@ -176,14 +191,6 @@ function SuperBlockIntro({
|
||||
{superBlockIntroText.map((str, i) => (
|
||||
<p dangerouslySetInnerHTML={{ __html: str }} key={i} />
|
||||
))}
|
||||
{superBlockNoteText && (
|
||||
<>
|
||||
<Spacer size='m' />
|
||||
<Callout variant='note' label={t('misc.note')}>
|
||||
{superBlockNoteText}
|
||||
</Callout>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -192,6 +199,7 @@ function SuperBlockIntro({
|
||||
<IntroTopDefault fsd={superBlock === SuperBlocks.FullStackDeveloperV9} />
|
||||
<ConditionalDonationAlert
|
||||
superBlock={superBlock}
|
||||
superBlockNoteText={superBlockNoteText}
|
||||
onCertificationDonationAlertClick={onCertificationDonationAlertClick}
|
||||
isDonating={isDonating}
|
||||
/>
|
||||
|
||||
@@ -82,8 +82,23 @@ const translationMap: Record<string, unknown> = {
|
||||
intro: ['Create responsive layouts across devices.'],
|
||||
note: ''
|
||||
},
|
||||
'intro:a2-english-for-developers': {
|
||||
title: 'A2 English for Developers',
|
||||
intro: ['Learn workplace English at the A2 level.'],
|
||||
note: 'This certification is currently in beta.'
|
||||
},
|
||||
'intro:front-end-development-libraries-v9': {
|
||||
title: 'Front-End Development Libraries Certification',
|
||||
intro: ['Learn the libraries developers use to build webpages.'],
|
||||
note: ''
|
||||
},
|
||||
'misc.fsd-b-cta': 'Start Learning',
|
||||
'misc.continue-learning': 'Continue Learning'
|
||||
'misc.continue-learning': 'Continue Learning',
|
||||
'donate.consider-donating':
|
||||
'Please consider donating to support the completion of its development.',
|
||||
'donate.consider-donating-2':
|
||||
'If you want to help us speed up development of this curriculum, please consider becoming a supporter of our charity.',
|
||||
'buttons.donate-now': 'Donate Now'
|
||||
};
|
||||
|
||||
const mockT = vi.fn((key: string, options?: { returnObjects?: boolean }) => {
|
||||
@@ -451,6 +466,100 @@ describe('SuperBlockIntroductionPage', () => {
|
||||
expect(cta).toHaveAttribute('href', nextChallenge.fields.slug);
|
||||
});
|
||||
|
||||
describe('note and donation callout', () => {
|
||||
const renderForSuperBlock = ({
|
||||
superBlock,
|
||||
isDonating
|
||||
}: {
|
||||
superBlock: SuperBlocks;
|
||||
isDonating: boolean;
|
||||
}) => {
|
||||
const setup = createSetup(superBlock);
|
||||
const props = createPageProps(setup, superBlock, {
|
||||
user: {
|
||||
completedChallenges: [],
|
||||
isDonating
|
||||
}
|
||||
});
|
||||
render(<SuperBlockIntroductionPage {...props} />);
|
||||
};
|
||||
|
||||
it('should render the note text for a certification with a note', async () => {
|
||||
renderForSuperBlock({
|
||||
superBlock: SuperBlocks.A2English,
|
||||
isDonating: false
|
||||
});
|
||||
|
||||
expect(
|
||||
await screen.findByText('This certification is currently in beta.')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render the beta donation copy and Donate Now button for a non-donor on a beta certification', async () => {
|
||||
renderForSuperBlock({
|
||||
superBlock: SuperBlocks.A2English,
|
||||
isDonating: false
|
||||
});
|
||||
|
||||
await screen.findByText('This certification is currently in beta.');
|
||||
expect(
|
||||
screen.getByText(
|
||||
'Please consider donating to support the completion of its development.'
|
||||
)
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByRole('link', { name: 'Donate Now' })
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should not render the donation copy or Donate Now button for a donor on a beta certification', async () => {
|
||||
renderForSuperBlock({
|
||||
superBlock: SuperBlocks.A2English,
|
||||
isDonating: true
|
||||
});
|
||||
|
||||
await screen.findByText('This certification is currently in beta.');
|
||||
expect(
|
||||
screen.queryByText(
|
||||
'Please consider donating to support the completion of its development.'
|
||||
)
|
||||
).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByRole('link', { name: 'Donate Now' })
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render the unfinished-certification donation copy and Donate Now button for a non-donor on an unfinished certification', async () => {
|
||||
renderForSuperBlock({
|
||||
superBlock: SuperBlocks.FrontEndDevLibsV9,
|
||||
isDonating: false
|
||||
});
|
||||
|
||||
expect(
|
||||
await screen.findByRole('link', { name: 'Donate Now' })
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByText('placeholder')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByText(
|
||||
'Please consider donating to support the completion of its development.'
|
||||
)
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should not render any callout when the certification has no note and is not eligible for the donation callout', async () => {
|
||||
renderForSuperBlock({
|
||||
superBlock: SuperBlocks.RespWebDesign,
|
||||
isDonating: false
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.queryByRole('link', { name: 'Donate Now' })
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it.each(scenariosWithoutCta)('$description', async scenario => {
|
||||
const { superBlock, completedOrders } = scenario;
|
||||
const setup = createSetup(superBlock);
|
||||
|
||||
@@ -16,10 +16,10 @@ import {
|
||||
SuperBlocks,
|
||||
certificationCollectionSuperBlocks
|
||||
} from '@freecodecamp/shared/config/curriculum';
|
||||
import callGA from '../../analytics/call-ga';
|
||||
import DonateModal from '../../components/Donation/donation-modal';
|
||||
import Login from '../../components/Header/components/login';
|
||||
import Map from '../../components/Map';
|
||||
import callGA from '../../analytics/call-ga';
|
||||
import { tryToShowDonationModal } from '../../redux/actions';
|
||||
import {
|
||||
isSignedInSelector,
|
||||
|
||||
Reference in New Issue
Block a user