mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-05-28 18:26:54 +00:00
feat(ui): view challenge source link (#66305)
Co-authored-by: Mrugesh Mohapatra <1884376+raisedadead@users.noreply.github.com> Co-authored-by: Ahmad Abdolsaheb <ahmad.abdolsaheb@gmail.com>
This commit is contained in:
@@ -2,3 +2,4 @@ export const MAX_MOBILE_WIDTH = 767;
|
|||||||
export const EX_SMALL_VIEWPORT_HEIGHT = 300;
|
export const EX_SMALL_VIEWPORT_HEIGHT = 300;
|
||||||
export const TOOL_PANEL_HEIGHT = 37;
|
export const TOOL_PANEL_HEIGHT = 37;
|
||||||
export const SEARCH_EXPOSED_WIDTH = 980;
|
export const SEARCH_EXPOSED_WIDTH = 980;
|
||||||
|
export const GITHUB_LOCATION = 'https://github.com/freeCodeCamp';
|
||||||
|
|||||||
@@ -125,6 +125,7 @@
|
|||||||
"sign-in-with-google": "Sign in with Google",
|
"sign-in-with-google": "Sign in with Google",
|
||||||
"go-to-dcc-today": "Go to Today's Challenge",
|
"go-to-dcc-today": "Go to Today's Challenge",
|
||||||
"go-to-dcc-archive": "Go to Daily Coding Challenge Archive",
|
"go-to-dcc-archive": "Go to Daily Coding Challenge Archive",
|
||||||
|
"challenge-source": "View Challenge Source",
|
||||||
"outline": "Outline"
|
"outline": "Outline"
|
||||||
},
|
},
|
||||||
"daily-coding-challenges": {
|
"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.",
|
"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-one": "Replace these two sentences with your copied code.",
|
||||||
"add-code-two": "Please leave the ``` line above and the ``` line below,",
|
"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": {
|
"user-token": {
|
||||||
"title": "User Token",
|
"title": "User Token",
|
||||||
|
|||||||
@@ -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'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -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();
|
||||||
|
};
|
||||||
@@ -203,6 +203,7 @@ function ShowClassic({
|
|||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
instructions,
|
instructions,
|
||||||
|
id,
|
||||||
hooks,
|
hooks,
|
||||||
tests,
|
tests,
|
||||||
challengeType,
|
challengeType,
|
||||||
@@ -421,6 +422,8 @@ function ShowClassic({
|
|||||||
description={description}
|
description={description}
|
||||||
instructions={instructions}
|
instructions={instructions}
|
||||||
superBlock={superBlock}
|
superBlock={superBlock}
|
||||||
|
challengeId={id}
|
||||||
|
block={block}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
challengeTitle={
|
challengeTitle={
|
||||||
|
|||||||
@@ -2,17 +2,22 @@ import React, { useEffect } from 'react';
|
|||||||
import { initializeMathJax, isMathJaxAllowed } from '../../../utils/math-jax';
|
import { initializeMathJax, isMathJaxAllowed } from '../../../utils/math-jax';
|
||||||
import PrismFormatted from './prism-formatted';
|
import PrismFormatted from './prism-formatted';
|
||||||
import './challenge-description.css';
|
import './challenge-description.css';
|
||||||
|
import { generateGithubLink } from '../../../components/create-github-link';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
description?: string;
|
description?: string;
|
||||||
instructions?: string;
|
instructions?: string;
|
||||||
superBlock?: string;
|
superBlock?: string;
|
||||||
|
challengeId: string;
|
||||||
|
block: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ChallengeDescription = ({
|
const ChallengeDescription = ({
|
||||||
description,
|
description,
|
||||||
instructions,
|
instructions,
|
||||||
superBlock
|
superBlock,
|
||||||
|
challengeId,
|
||||||
|
block
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (superBlock && isMathJaxAllowed(superBlock)) {
|
if (superBlock && isMathJaxAllowed(superBlock)) {
|
||||||
@@ -20,10 +25,12 @@ const ChallengeDescription = ({
|
|||||||
}
|
}
|
||||||
}, [superBlock]);
|
}, [superBlock]);
|
||||||
|
|
||||||
|
const githubLink = generateGithubLink(challengeId, block);
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={'challenge-instructions mathjax-support'}
|
className={'challenge-instructions mathjax-support'}
|
||||||
data-playwright-test-label='challenge-description'
|
data-playwright-test-label='challenge-description'
|
||||||
|
data-github-link={githubLink}
|
||||||
>
|
>
|
||||||
{description && <PrismFormatted text={description} />}
|
{description && <PrismFormatted text={description} />}
|
||||||
{instructions && description && <hr />}
|
{instructions && description && <hr />}
|
||||||
|
|||||||
@@ -120,6 +120,7 @@ const ShowGeneric = ({
|
|||||||
explanation,
|
explanation,
|
||||||
challengeType,
|
challengeType,
|
||||||
helpCategory,
|
helpCategory,
|
||||||
|
id,
|
||||||
instructions,
|
instructions,
|
||||||
questions,
|
questions,
|
||||||
tests,
|
tests,
|
||||||
@@ -297,6 +298,8 @@ const ShowGeneric = ({
|
|||||||
<ChallengeDescription
|
<ChallengeDescription
|
||||||
description={description}
|
description={description}
|
||||||
superBlock={superBlock}
|
superBlock={superBlock}
|
||||||
|
block={block}
|
||||||
|
challengeId={id}
|
||||||
/>
|
/>
|
||||||
<Spacer size='m' />
|
<Spacer size='m' />
|
||||||
</Col>
|
</Col>
|
||||||
@@ -336,6 +339,8 @@ const ShowGeneric = ({
|
|||||||
<ChallengeDescription
|
<ChallengeDescription
|
||||||
instructions={instructions}
|
instructions={instructions}
|
||||||
superBlock={superBlock}
|
superBlock={superBlock}
|
||||||
|
block={block}
|
||||||
|
challengeId={id}
|
||||||
/>
|
/>
|
||||||
<Spacer size='m' />
|
<Spacer size='m' />
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -139,6 +139,7 @@ function MsTrophy(props: MsTrophyProps) {
|
|||||||
description,
|
description,
|
||||||
instructions,
|
instructions,
|
||||||
superBlock,
|
superBlock,
|
||||||
|
id,
|
||||||
block,
|
block,
|
||||||
translationPending
|
translationPending
|
||||||
}
|
}
|
||||||
@@ -175,6 +176,8 @@ function MsTrophy(props: MsTrophyProps) {
|
|||||||
superBlock={superBlock}
|
superBlock={superBlock}
|
||||||
description={description}
|
description={description}
|
||||||
instructions={instructions}
|
instructions={instructions}
|
||||||
|
block={block}
|
||||||
|
challengeId={id}
|
||||||
/>
|
/>
|
||||||
<LinkMsUser />
|
<LinkMsUser />
|
||||||
<hr />
|
<hr />
|
||||||
|
|||||||
@@ -144,6 +144,7 @@ const ShowBackEnd = (props: BackEndProps) => {
|
|||||||
challengeType,
|
challengeType,
|
||||||
forumTopicId,
|
forumTopicId,
|
||||||
title,
|
title,
|
||||||
|
id,
|
||||||
description,
|
description,
|
||||||
instructions,
|
instructions,
|
||||||
translationPending,
|
translationPending,
|
||||||
@@ -183,6 +184,8 @@ const ShowBackEnd = (props: BackEndProps) => {
|
|||||||
superBlock={superBlock}
|
superBlock={superBlock}
|
||||||
description={description}
|
description={description}
|
||||||
instructions={instructions}
|
instructions={instructions}
|
||||||
|
block={block}
|
||||||
|
challengeId={id}
|
||||||
/>
|
/>
|
||||||
<Spacer size='m' />
|
<Spacer size='m' />
|
||||||
<SolutionForm
|
<SolutionForm
|
||||||
|
|||||||
@@ -120,6 +120,7 @@ const ShowFrontEndProject = (props: ProjectProps) => {
|
|||||||
challengeType,
|
challengeType,
|
||||||
forumTopicId,
|
forumTopicId,
|
||||||
title,
|
title,
|
||||||
|
id,
|
||||||
description,
|
description,
|
||||||
instructions,
|
instructions,
|
||||||
superBlock,
|
superBlock,
|
||||||
@@ -157,6 +158,8 @@ const ShowFrontEndProject = (props: ProjectProps) => {
|
|||||||
superBlock={superBlock}
|
superBlock={superBlock}
|
||||||
description={description}
|
description={description}
|
||||||
instructions={instructions}
|
instructions={instructions}
|
||||||
|
block={block}
|
||||||
|
challengeId={id}
|
||||||
/>
|
/>
|
||||||
<Spacer size='m' />
|
<Spacer size='m' />
|
||||||
<SolutionForm
|
<SolutionForm
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ const ShowQuiz = ({
|
|||||||
description,
|
description,
|
||||||
challengeType,
|
challengeType,
|
||||||
helpCategory,
|
helpCategory,
|
||||||
|
id,
|
||||||
superBlock,
|
superBlock,
|
||||||
block,
|
block,
|
||||||
tests,
|
tests,
|
||||||
@@ -356,6 +357,8 @@ const ShowQuiz = ({
|
|||||||
<ChallengeDescription
|
<ChallengeDescription
|
||||||
description={description}
|
description={description}
|
||||||
superBlock={superBlock}
|
superBlock={superBlock}
|
||||||
|
block={block}
|
||||||
|
challengeId={id}
|
||||||
/>
|
/>
|
||||||
<Spacer size='l' />
|
<Spacer size='l' />
|
||||||
<ObserveKeys>
|
<ObserveKeys>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
challengeMetaSelector,
|
challengeMetaSelector,
|
||||||
projectFormValuesSelector
|
projectFormValuesSelector
|
||||||
} from './selectors';
|
} from './selectors';
|
||||||
|
import { generateGithubLink } from '../../../components/create-github-link';
|
||||||
|
|
||||||
const { forumLocation } = envData;
|
const { forumLocation } = envData;
|
||||||
|
|
||||||
@@ -131,7 +132,8 @@ function createQuestionEpic(action$, state$, { window }) {
|
|||||||
superBlock,
|
superBlock,
|
||||||
block,
|
block,
|
||||||
helpCategory,
|
helpCategory,
|
||||||
challengeType
|
challengeType,
|
||||||
|
id
|
||||||
} = challengeMetaSelector(state);
|
} = challengeMetaSelector(state);
|
||||||
|
|
||||||
challengeFiles = insertEditableRegions(challengeFiles);
|
challengeFiles = insertEditableRegions(challengeFiles);
|
||||||
@@ -156,13 +158,18 @@ function createQuestionEpic(action$, state$, { window }) {
|
|||||||
projectFormValuesSelector(state)
|
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 browserInfoHeading = i18next.t('forum-help.browser-info');
|
||||||
const userAgentHeading = i18next.t('forum-help.user-agent', {
|
const userAgentHeading = i18next.t('forum-help.user-agent', {
|
||||||
userAgent
|
userAgent
|
||||||
});
|
});
|
||||||
const challengeHeading = i18next.t('forum-help.challenge');
|
const challengeHeading = i18next.t('forum-help.challenge');
|
||||||
const blockTitle = i18next.t(`intro:${superBlock}.blocks.${block}.title`);
|
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)
|
const camperCodeHeading = nonCodeChallenges.includes(challengeType)
|
||||||
? ''
|
? ''
|
||||||
|
|||||||
Reference in New Issue
Block a user