diff --git a/client/config/misc.ts b/client/config/misc.ts index d2fb61fa9a1..ca86710db00 100644 --- a/client/config/misc.ts +++ b/client/config/misc.ts @@ -2,3 +2,4 @@ export const MAX_MOBILE_WIDTH = 767; export const EX_SMALL_VIEWPORT_HEIGHT = 300; export const TOOL_PANEL_HEIGHT = 37; export const SEARCH_EXPOSED_WIDTH = 980; +export const GITHUB_LOCATION = 'https://github.com/freeCodeCamp'; diff --git a/client/i18n/locales/english/translations.json b/client/i18n/locales/english/translations.json index 641dc9348f5..8360bd5a5e9 100644 --- a/client/i18n/locales/english/translations.json +++ b/client/i18n/locales/english/translations.json @@ -125,6 +125,7 @@ "sign-in-with-google": "Sign in with Google", "go-to-dcc-today": "Go to Today's Challenge", "go-to-dcc-archive": "Go to Daily Coding Challenge Archive", + "challenge-source": "View Challenge Source", "outline": "Outline" }, "daily-coding-challenges": { @@ -1421,7 +1422,8 @@ "too-long-three": "Please copy/paste all the editor code showing in the challenge from where you just linked.", "add-code-one": "Replace these two sentences with your copied code.", "add-code-two": "Please leave the ``` line above and the ``` line below,", - "add-code-three": "because they allow your code to properly format in the post." + "add-code-three": "because they allow your code to properly format in the post.", + "git-info": "Github Link: {{gitLink}}" }, "user-token": { "title": "User Token", diff --git a/client/src/components/create-github-link.test.ts b/client/src/components/create-github-link.test.ts new file mode 100644 index 00000000000..47e9bd6d5f7 --- /dev/null +++ b/client/src/components/create-github-link.test.ts @@ -0,0 +1,39 @@ +import { describe, it, expect, vi, afterEach } from 'vitest'; + +describe('generateGitHubLink', () => { + afterEach(() => vi.resetModules()); + it('should return a link to a challenge for an english block', async () => { + vi.doMock('../../config/env.json', () => ({ + default: { + curriculumLocale: 'english' + } + })); + const { generateGithubLink } = await import('./create-github-link'); + const link = generateGithubLink( + '5d5a813321b9e3db6c106a46', + 'learn-basic-javascript-by-building-a-role-playing-game' + ); + + expect(link).toBe( + 'https://github.com/freeCodeCamp/freeCodeCamp/blob/main/curriculum/challenges/english/blocks/learn-basic-javascript-by-building-a-role-playing-game/5d5a813321b9e3db6c106a46.md' + ); + }); + + it('should return a link for a challenge in the Spanish curriculum', async () => { + vi.doMock('../../config/env.json', () => ({ + default: { + curriculumLocale: 'espanol' + } + })); + const { generateGithubLink } = await import('./create-github-link'); + + const link = generateGithubLink( + '5d5a813321b9e3db6c106a46', + 'learn-basic-javascript-by-building-a-role-playing-game' + ); + + expect(link).toBe( + 'https://github.com/freeCodeCamp/i18n-curriculum/blob/main/curriculum/challenges/espanol/blocks/learn-basic-javascript-by-building-a-role-playing-game/5d5a813321b9e3db6c106a46.md' + ); + }); +}); diff --git a/client/src/components/create-github-link.ts b/client/src/components/create-github-link.ts new file mode 100644 index 00000000000..418dd586234 --- /dev/null +++ b/client/src/components/create-github-link.ts @@ -0,0 +1,22 @@ +import envData from '../../config/env.json'; +import { GITHUB_LOCATION } from '../../config/misc'; + +const { curriculumLocale } = envData; + +export const generateGithubLink = (challengeId: string, block: string) => { + const repository = + curriculumLocale === 'english' ? '/freeCodeCamp' : '/i18n-curriculum'; + const gitURL = new URL(GITHUB_LOCATION); + + gitURL.pathname = + gitURL.pathname + + [ + repository, + 'blob/main/curriculum/challenges', + curriculumLocale, + 'blocks', + block, + `${challengeId}.md` + ].join('/'); + return gitURL.toString(); +}; diff --git a/client/src/templates/Challenges/classic/show.tsx b/client/src/templates/Challenges/classic/show.tsx index 2a5990c0193..6a5bc3028ba 100644 --- a/client/src/templates/Challenges/classic/show.tsx +++ b/client/src/templates/Challenges/classic/show.tsx @@ -203,6 +203,7 @@ function ShowClassic({ title, description, instructions, + id, hooks, tests, challengeType, @@ -421,6 +422,8 @@ function ShowClassic({ description={description} instructions={instructions} superBlock={superBlock} + challengeId={id} + block={block} /> } challengeTitle={ diff --git a/client/src/templates/Challenges/components/challenge-description.tsx b/client/src/templates/Challenges/components/challenge-description.tsx index 37adc64d397..98e2d6dbb29 100644 --- a/client/src/templates/Challenges/components/challenge-description.tsx +++ b/client/src/templates/Challenges/components/challenge-description.tsx @@ -2,17 +2,22 @@ import React, { useEffect } from 'react'; import { initializeMathJax, isMathJaxAllowed } from '../../../utils/math-jax'; import PrismFormatted from './prism-formatted'; import './challenge-description.css'; +import { generateGithubLink } from '../../../components/create-github-link'; type Props = { description?: string; instructions?: string; superBlock?: string; + challengeId: string; + block: string; }; const ChallengeDescription = ({ description, instructions, - superBlock + superBlock, + challengeId, + block }: Props) => { useEffect(() => { if (superBlock && isMathJaxAllowed(superBlock)) { @@ -20,10 +25,12 @@ const ChallengeDescription = ({ } }, [superBlock]); + const githubLink = generateGithubLink(challengeId, block); return (
{description && } {instructions && description &&
} diff --git a/client/src/templates/Challenges/generic/show.tsx b/client/src/templates/Challenges/generic/show.tsx index e4bd99138b3..b786b359cd3 100644 --- a/client/src/templates/Challenges/generic/show.tsx +++ b/client/src/templates/Challenges/generic/show.tsx @@ -120,6 +120,7 @@ const ShowGeneric = ({ explanation, challengeType, helpCategory, + id, instructions, questions, tests, @@ -297,6 +298,8 @@ const ShowGeneric = ({ @@ -336,6 +339,8 @@ const ShowGeneric = ({ diff --git a/client/src/templates/Challenges/ms-trophy/show.tsx b/client/src/templates/Challenges/ms-trophy/show.tsx index 84766e3027b..df762c63d2a 100644 --- a/client/src/templates/Challenges/ms-trophy/show.tsx +++ b/client/src/templates/Challenges/ms-trophy/show.tsx @@ -139,6 +139,7 @@ function MsTrophy(props: MsTrophyProps) { description, instructions, superBlock, + id, block, translationPending } @@ -175,6 +176,8 @@ function MsTrophy(props: MsTrophyProps) { superBlock={superBlock} description={description} instructions={instructions} + block={block} + challengeId={id} />
diff --git a/client/src/templates/Challenges/projects/backend/show.tsx b/client/src/templates/Challenges/projects/backend/show.tsx index c238197c44f..178959efcd4 100644 --- a/client/src/templates/Challenges/projects/backend/show.tsx +++ b/client/src/templates/Challenges/projects/backend/show.tsx @@ -144,6 +144,7 @@ const ShowBackEnd = (props: BackEndProps) => { challengeType, forumTopicId, title, + id, description, instructions, translationPending, @@ -183,6 +184,8 @@ const ShowBackEnd = (props: BackEndProps) => { superBlock={superBlock} description={description} instructions={instructions} + block={block} + challengeId={id} /> { challengeType, forumTopicId, title, + id, description, instructions, superBlock, @@ -157,6 +158,8 @@ const ShowFrontEndProject = (props: ProjectProps) => { superBlock={superBlock} description={description} instructions={instructions} + block={block} + challengeId={id} /> diff --git a/client/src/templates/Challenges/redux/create-question-epic.js b/client/src/templates/Challenges/redux/create-question-epic.js index 01135a1edce..745e8c4823b 100644 --- a/client/src/templates/Challenges/redux/create-question-epic.js +++ b/client/src/templates/Challenges/redux/create-question-epic.js @@ -12,6 +12,7 @@ import { challengeMetaSelector, projectFormValuesSelector } from './selectors'; +import { generateGithubLink } from '../../../components/create-github-link'; const { forumLocation } = envData; @@ -131,7 +132,8 @@ function createQuestionEpic(action$, state$, { window }) { superBlock, block, helpCategory, - challengeType + challengeType, + id } = challengeMetaSelector(state); challengeFiles = insertEditableRegions(challengeFiles); @@ -156,13 +158,18 @@ function createQuestionEpic(action$, state$, { window }) { projectFormValuesSelector(state) ); + const gitLink = generateGithubLink(id, block); + const gitInfo = i18next.t('forum-help.git-info', { + gitLink + }); + const browserInfoHeading = i18next.t('forum-help.browser-info'); const userAgentHeading = i18next.t('forum-help.user-agent', { userAgent }); const challengeHeading = i18next.t('forum-help.challenge'); const blockTitle = i18next.t(`intro:${superBlock}.blocks.${block}.title`); - const endingText = `### ${browserInfoHeading}\n\n${userAgentHeading}\n\n### ${challengeHeading}\n${blockTitle} - ${challengeTitle}\n${challengeUrl}`; + const endingText = `### ${browserInfoHeading}\n\n${userAgentHeading}\n\n### ${challengeHeading}\n${blockTitle} - ${challengeTitle}\n${challengeUrl}\n${gitInfo}`; const camperCodeHeading = nonCodeChallenges.includes(challengeType) ? ''