refactor: stop spreading challenge over the node (#44499)

* refactor: stop spreading challenge over the node

Instead of creating a Gatsby node with id of challenge.id, we create a
single node field that has all the challenge data.

While this makes the GraphQL queries more verbose, it means we're free
to create multiple nodes with the same challenge.id.

* test: update time-line test for new GQL schema

* test: update mocks with new GQL schema
This commit is contained in:
Oliver Eyton-Williams
2021-12-14 19:11:20 +01:00
committed by GitHub
parent 755f27093c
commit 3b560deab6
18 changed files with 593 additions and 440 deletions

View File

@ -20,13 +20,6 @@ const createByIdentityMap = {
exports.onCreateNode = function onCreateNode({ node, actions, getNode }) {
const { createNodeField } = actions;
if (node.internal.type === 'ChallengeNode') {
const { tests = [], block, dashedName, superBlock } = node;
const slug = `/learn/${superBlock}/${block}/${dashedName}`;
createNodeField({ node, name: 'slug', value: slug });
createNodeField({ node, name: 'blockName', value: blockNameify(block) });
createNodeField({ node, name: 'tests', value: tests });
}
if (node.internal.type === 'MarkdownRemark') {
const slug = createFilePath({ node, getNode });
@ -71,10 +64,17 @@ exports.createPages = function createPages({ graphql, actions, reporter }) {
graphql(`
{
allChallengeNode(
sort: { fields: [superOrder, order, challengeOrder] }
sort: {
fields: [
challenge___superOrder
challenge___order
challenge___challengeOrder
]
}
) {
edges {
node {
challenge {
block
challengeType
fields {
@ -105,6 +105,7 @@ exports.createPages = function createPages({ graphql, actions, reporter }) {
}
}
}
}
allMarkdownRemark {
edges {
node {
@ -137,12 +138,22 @@ exports.createPages = function createPages({ graphql, actions, reporter }) {
);
const blocks = uniq(
result.data.allChallengeNode.edges.map(({ node: { block } }) => block)
result.data.allChallengeNode.edges.map(
({
node: {
challenge: { block }
}
}) => block
)
).map(block => blockNameify(block));
const superBlocks = uniq(
result.data.allChallengeNode.edges.map(
({ node: { superBlock } }) => superBlock
({
node: {
challenge: { superBlock }
}
}) => superBlock
)
);
@ -256,6 +267,9 @@ exports.createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions;
const typeDefs = `
type ChallengeNode implements Node {
challenge: Challenge
}
type Challenge {
challengeFiles: [FileContents]
notes: String
url: String

View File

@ -1,4 +1,5 @@
const crypto = require('crypto');
const { blockNameify } = require('../../../utils/block-nameify');
function createChallengeNode(challenge, reporter) {
// challengeType 11 is for video challenges (they only have instructions)
@ -31,6 +32,17 @@ function createChallengeNode(challenge, reporter) {
type: challenge.challengeType === 7 ? 'CertificateNode' : 'ChallengeNode'
};
if (internal.type === 'ChallengeNode') {
const { tests = [], block, dashedName, superBlock } = challenge;
const slug = `/learn/${superBlock}/${block}/${dashedName}`;
challenge.fields = {
slug,
blockName: blockNameify(block),
tests
};
}
return JSON.parse(
JSON.stringify(
Object.assign(
@ -41,7 +53,8 @@ function createChallengeNode(challenge, reporter) {
internal,
sourceInstanceName: 'challenge'
},
challenge
{ challenge },
{ id: crypto.randomUUID() }
)
)
);

View File

@ -1,5 +1,6 @@
const mockChallengeNodes = [
{
challenge: {
fields: {
slug: '/super-block-one/block-a/challenge-one',
blockName: 'Block A'
@ -10,8 +11,10 @@ const mockChallengeNodes = [
isPrivate: false,
superBlock: 'super-block-one',
dashedName: 'challenge-one'
}
},
{
challenge: {
fields: {
slug: '/super-block-one/block-a/challenge-two',
blockName: 'Block A'
@ -22,8 +25,10 @@ const mockChallengeNodes = [
isPrivate: false,
superBlock: 'super-block-one',
dashedName: 'challenge-two'
}
},
{
challenge: {
fields: {
slug: '/super-block-one/block-b/challenge-one',
blockName: 'Block B'
@ -34,8 +39,10 @@ const mockChallengeNodes = [
isPrivate: false,
superBlock: 'super-block-one',
dashedName: 'challenge-one'
}
},
{
challenge: {
fields: {
slug: '/super-block-one/block-b/challenge-two',
blockName: 'Block B'
@ -47,8 +54,10 @@ const mockChallengeNodes = [
isPrivate: false,
superBlock: 'super-block-one',
dashedName: 'challenge-two'
}
},
{
challenge: {
fields: {
slug: '/super-block-one/block-c/challenge-one',
blockName: 'Block C'
@ -59,8 +68,10 @@ const mockChallengeNodes = [
isPrivate: true,
superBlock: 'super-block-one',
dashedName: 'challenge-one'
}
},
{
challenge: {
fields: {
slug: '/super-block-two/block-a/challenge-one',
blockName: 'Block A'
@ -71,8 +82,10 @@ const mockChallengeNodes = [
isPrivate: false,
superBlock: 'super-block-two',
dashedName: 'challenge-one'
}
},
{
challenge: {
fields: {
slug: '/super-block-two/block-a/challenge-two',
blockName: 'Block A'
@ -83,8 +96,10 @@ const mockChallengeNodes = [
isPrivate: false,
superBlock: 'super-block-two',
dashedName: 'challenge-two'
}
},
{
challenge: {
fields: {
slug: '/super-block-two/block-b/challenge-one',
blockName: 'Block B'
@ -95,8 +110,10 @@ const mockChallengeNodes = [
isPrivate: false,
superBlock: 'super-block-two',
dashedName: 'challenge-one'
}
},
{
challenge: {
fields: {
slug: '/super-block-two/block-b/challenge-two',
blockName: 'Block B'
@ -107,8 +124,10 @@ const mockChallengeNodes = [
isPrivate: false,
superBlock: 'super-block-two',
dashedName: 'challenge-two'
}
},
{
challenge: {
fields: {
slug: '/super-block-three/block-a/challenge-one',
blockName: 'Block A'
@ -119,8 +138,10 @@ const mockChallengeNodes = [
isPrivate: false,
superBlock: 'super-block-three',
dashedName: 'challenge-one'
}
},
{
challenge: {
fields: {
slug: '/super-block-three/block-c/challenge-two',
blockName: 'Block C'
@ -132,6 +153,7 @@ const mockChallengeNodes = [
superBlock: 'super-block-three',
dashedName: 'challenge-two'
}
}
];
export default mockChallengeNodes;

View File

@ -42,19 +42,19 @@ const linkSpacingStyle = {
function renderLandingMap(nodes: ChallengeNode[]) {
nodes = nodes.filter(
node => node.superBlock !== SuperBlocks.CodingInterviewPrep
({ challenge }) => challenge.superBlock !== SuperBlocks.CodingInterviewPrep
);
return (
<ul data-test-label='certifications'>
{nodes.map((node, i) => (
{nodes.map(({ challenge }, i) => (
<li key={i}>
<Link
className='btn link-btn btn-lg'
to={`/learn/${node.superBlock}/`}
to={`/learn/${challenge.superBlock}/`}
>
<div style={linkSpacingStyle}>
{generateIconComponent(node.superBlock, 'map-icon')}
{i18next.t(`intro:${node.superBlock}.title`)}
{generateIconComponent(challenge.superBlock, 'map-icon')}
{i18next.t(`intro:${challenge.superBlock}.title`)}
</div>
<LinkButton />
</Link>
@ -68,18 +68,20 @@ function renderLearnMap(
nodes: ChallengeNode[],
currentSuperBlock: MapProps['currentSuperBlock']
) {
nodes = nodes.filter(node => node.superBlock !== currentSuperBlock);
nodes = nodes.filter(
({ challenge }) => challenge.superBlock !== currentSuperBlock
);
return curriculumLocale === 'english' ? (
<ul data-test-label='learn-curriculum-map'>
{nodes.map((node, i) => (
{nodes.map(({ challenge }, i) => (
<li key={i}>
<Link
className='btn link-btn btn-lg'
to={`/learn/${node.superBlock}/`}
to={`/learn/${challenge.superBlock}/`}
>
<div style={linkSpacingStyle}>
{generateIconComponent(node.superBlock, 'map-icon')}
{createSuperBlockTitle(node.superBlock)}
{generateIconComponent(challenge.superBlock, 'map-icon')}
{createSuperBlockTitle(challenge.superBlock)}
</div>
</Link>
</li>
@ -88,16 +90,18 @@ function renderLearnMap(
) : (
<ul data-test-label='learn-curriculum-map'>
{nodes
.filter(node => isAuditedCert(curriculumLocale, node.superBlock))
.map((node, i) => (
.filter(({ challenge }) =>
isAuditedCert(curriculumLocale, challenge.superBlock)
)
.map(({ challenge }, i) => (
<li key={i}>
<Link
className='btn link-btn btn-lg'
to={`/learn/${node.superBlock}/`}
to={`/learn/${challenge.superBlock}/`}
>
<div style={linkSpacingStyle}>
{generateIconComponent(node.superBlock, 'map-icon')}
{createSuperBlockTitle(node.superBlock)}
{generateIconComponent(challenge.superBlock, 'map-icon')}
{createSuperBlockTitle(challenge.superBlock)}
</div>
</Link>
</li>
@ -115,16 +119,19 @@ function renderLearnMap(
<Spacer />
</div>
{nodes
.filter(node => !isAuditedCert(curriculumLocale, node.superBlock))
.map((node, i) => (
.filter(
({ challenge }) =>
!isAuditedCert(curriculumLocale, challenge.superBlock)
)
.map(({ challenge }, i) => (
<li key={i}>
<Link
className='btn link-btn btn-lg'
to={`/learn/${node.superBlock}/`}
to={`/learn/${challenge.superBlock}/`}
>
<div style={linkSpacingStyle}>
{generateIconComponent(node.superBlock, 'map-icon')}
{createSuperBlockTitle(node.superBlock)}
{generateIconComponent(challenge.superBlock, 'map-icon')}
{createSuperBlockTitle(challenge.superBlock)}
</div>
</Link>
</li>
@ -145,15 +152,17 @@ export function Map({
const data: MapData = useStaticQuery(graphql`
query SuperBlockNodes {
allChallengeNode(
sort: { fields: [superOrder] }
filter: { order: { eq: 0 }, challengeOrder: { eq: 0 } }
sort: { fields: [challenge___superOrder] }
filter: { challenge: { order: { eq: 0 }, challengeOrder: { eq: 0 } } }
) {
nodes {
challenge {
superBlock
dashedName
}
}
}
}
`);
const nodes = data.allChallengeNode.nodes;

View File

@ -269,6 +269,7 @@ function useIdToNameMap(): Map<string, string> {
allChallengeNode {
edges {
node {
challenge {
fields {
slug
}
@ -278,6 +279,7 @@ function useIdToNameMap(): Map<string, string> {
}
}
}
}
`);
const idToNameMap = new Map();
for (const id of getCertIds()) {
@ -289,6 +291,7 @@ function useIdToNameMap(): Map<string, string> {
edges.forEach(
({
node: {
challenge: {
// @ts-expect-error Graphql needs typing
id,
// @ts-expect-error Graphql needs typing
@ -296,6 +299,7 @@ function useIdToNameMap(): Map<string, string> {
// @ts-expect-error Graphql needs typing
fields: { slug }
}
}
}) => {
idToNameMap.set(id, { challengeTitle: title, challengePath: slug });
}

View File

@ -12,24 +12,29 @@ beforeEach(() => {
edges: [
{
node: {
challenge: {
fields: {
slug: ''
},
id: '5e46f802ac417301a38fb92b',
title: 'Page View Time Series Visualizer'
}
}
},
{
node: {
challenge: {
fields: {
slug: ''
},
id: '5e4f5c4b570f7e3a4949899f',
title: 'Sea Level Predictor'
}
}
},
{
node: {
challenge: {
fields: {
slug: ''
},
@ -37,6 +42,7 @@ beforeEach(() => {
title: 'Medical Data Visualizer'
}
}
}
]
}
}));

View File

@ -53,9 +53,11 @@ interface LearnPageProps {
user: User;
data: {
challengeNode: {
challenge: {
fields: Slug;
};
};
};
executeGA: (payload: Record<string, unknown>) => void;
}
@ -70,9 +72,11 @@ function LearnPage({
executeGA,
data: {
challengeNode: {
challenge: {
fields: { slug }
}
}
}
}: LearnPageProps) {
const { t } = useTranslation();
@ -117,10 +121,12 @@ export default connect(mapStateToProps, mapDispatchToProps)(LearnPage);
export const query = graphql`
query FirstChallenge {
challengeNode(order: { eq: 0 }, challengeOrder: { eq: 0 }) {
challengeNode(challenge: { order: { eq: 0 }, challengeOrder: { eq: 0 } }) {
challenge {
fields {
slug
}
}
}
}
`;

View File

@ -128,6 +128,7 @@ export interface VideoLocaleIds {
}
export type ChallengeNode = {
challenge: {
block: string;
challengeOrder: number;
challengeType: number;
@ -178,6 +179,7 @@ export type ChallengeNode = {
bilibiliIds?: BilibiliIds;
videoUrl: string;
};
};
export type AllChallengeNode = {
edges: [

View File

@ -212,7 +212,9 @@ class ShowClassic extends Component<ShowClassicProps, ShowClassicState> {
componentDidMount() {
const {
data: {
challengeNode: { title }
challengeNode: {
challenge: { title }
}
}
} = this.props;
this.initializeComponent(title);
@ -222,18 +224,22 @@ class ShowClassic extends Component<ShowClassicProps, ShowClassicState> {
const {
data: {
challengeNode: {
challenge: {
title: prevTitle,
fields: { tests: prevTests }
}
}
}
} = prevProps;
const {
data: {
challengeNode: {
challenge: {
title: currentTitle,
fields: { tests: currTests }
}
}
}
} = this.props;
if (prevTitle !== currentTitle || prevTests !== currTests) {
this.initializeComponent(currentTitle);
@ -250,12 +256,14 @@ class ShowClassic extends Component<ShowClassicProps, ShowClassicState> {
openModal,
data: {
challengeNode: {
challenge: {
challengeFiles,
fields: { tests },
challengeType,
removeComments,
helpCategory
}
}
},
pageContext: {
challengeMeta,
@ -282,7 +290,7 @@ class ShowClassic extends Component<ShowClassicProps, ShowClassicState> {
cancelTests();
}
getChallenge = () => this.props.data.challengeNode;
getChallenge = () => this.props.data.challengeNode.challenge;
getBlockNameTitle(t: TFunction) {
const { block, superBlock, title } = this.getChallenge();
@ -337,10 +345,12 @@ class ShowClassic extends Component<ShowClassicProps, ShowClassicState> {
challengeFiles,
data: {
challengeNode: {
challenge: {
fields: { tests },
usesMultifileEditor
}
}
}
} = this.props;
const { description, title } = this.getChallenge();
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
@ -489,7 +499,8 @@ export default connect(
export const query = graphql`
query ClassicChallenge($slug: String!) {
challengeNode(fields: { slug: { eq: $slug } }) {
challengeNode(challenge: { fields: { slug: { eq: $slug } } }) {
challenge {
block
title
description
@ -526,4 +537,5 @@ export const query = graphql`
}
}
}
}
`;

View File

@ -49,7 +49,9 @@ class ShowCodeAlly extends Component<ShowCodeAllyProps> {
const {
updateChallengeMeta,
data: {
challengeNode: { challengeType, title }
challengeNode: {
challenge: { challengeType, title }
}
},
pageContext: { challengeMeta }
} = this.props;
@ -60,10 +62,12 @@ class ShowCodeAlly extends Component<ShowCodeAllyProps> {
const {
data: {
challengeNode: {
challenge: {
title,
fields: { blockName },
url
}
}
},
webhookToken = null
} = this.props;
@ -94,7 +98,8 @@ export default connect(mapStateToProps, mapDispatchToProps)(ShowCodeAlly);
// GraphQL
export const query = graphql`
query CodeAllyChallenge($slug: String!) {
challengeNode(fields: { slug: { eq: $slug } }) {
challengeNode(challenge: { fields: { slug: { eq: $slug } } }) {
challenge {
title
challengeType
url
@ -103,4 +108,5 @@ export const query = graphql`
}
}
}
}
`;

View File

@ -280,9 +280,18 @@ const useCurrentBlockIds = (blockName: string) => {
allChallengeNode: { edges }
}: { allChallengeNode: AllChallengeNode } = useStaticQuery(graphql`
query getCurrentBlockNodes {
allChallengeNode(sort: { fields: [superOrder, order, challengeOrder] }) {
allChallengeNode(
sort: {
fields: [
challenge___superOrder
challenge___order
challenge___challengeOrder
]
}
) {
edges {
node {
challenge {
fields {
blockName
}
@ -291,12 +300,13 @@ const useCurrentBlockIds = (blockName: string) => {
}
}
}
}
`);
const currentBlockIds = edges
.filter(edge => edge.node.fields.blockName === blockName)
.filter(edge => edge.node.challenge.fields.blockName === blockName)
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
.map(edge => edge.node.id);
.map(edge => edge.node.challenge.id);
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return currentBlockIds;
};

View File

@ -123,18 +123,22 @@ class BackEnd extends Component<BackEndProps> {
const {
data: {
challengeNode: {
challenge: {
title: prevTitle,
fields: { tests: prevTests }
}
}
}
} = prevProps;
const {
data: {
challengeNode: {
challenge: {
title: currentTitle,
fields: { tests: currTests }
}
}
}
} = this.props;
if (prevTitle !== currentTitle || prevTests !== currTests) {
this.initializeComponent();
@ -149,11 +153,13 @@ class BackEnd extends Component<BackEndProps> {
updateChallengeMeta,
data: {
challengeNode: {
challenge: {
fields: { tests },
title,
challengeType,
helpCategory
}
}
},
pageContext: { challengeMeta }
} = this.props;
@ -182,6 +188,7 @@ class BackEnd extends Component<BackEndProps> {
const {
data: {
challengeNode: {
challenge: {
fields: { blockName },
challengeType,
forumTopicId,
@ -192,6 +199,7 @@ class BackEnd extends Component<BackEndProps> {
superBlock,
block
}
}
},
isChallengeCompleted,
output,
@ -278,7 +286,8 @@ export default connect(
export const query = graphql`
query BackendChallenge($slug: String!) {
challengeNode(fields: { slug: { eq: $slug } }) {
challengeNode(challenge: { fields: { slug: { eq: $slug } } }) {
challenge {
forumTopicId
title
description
@ -298,4 +307,5 @@ export const query = graphql`
}
}
}
}
`;

View File

@ -76,7 +76,9 @@ class Project extends Component<ProjectProps> {
const {
challengeMounted,
data: {
challengeNode: { title, challengeType, helpCategory }
challengeNode: {
challenge: { title, challengeType, helpCategory }
}
},
pageContext: { challengeMeta },
updateChallengeMeta
@ -94,13 +96,17 @@ class Project extends Component<ProjectProps> {
componentDidUpdate(prevProps: ProjectProps): void {
const {
data: {
challengeNode: { title: prevTitle }
challengeNode: {
challenge: { title: prevTitle }
}
}
} = prevProps;
const {
challengeMounted,
data: {
challengeNode: { title: currentTitle, challengeType, helpCategory }
challengeNode: {
challenge: { title: currentTitle, challengeType, helpCategory }
}
},
pageContext: { challengeMeta },
updateChallengeMeta
@ -130,6 +136,7 @@ class Project extends Component<ProjectProps> {
const {
data: {
challengeNode: {
challenge: {
challengeType,
fields: { blockName },
forumTopicId,
@ -140,6 +147,7 @@ class Project extends Component<ProjectProps> {
block,
translationPending
}
}
},
isChallengeCompleted,
pageContext: {
@ -215,7 +223,8 @@ export default connect(
export const query = graphql`
query ProjectChallenge($slug: String!) {
challengeNode(fields: { slug: { eq: $slug } }) {
challengeNode(challenge: { fields: { slug: { eq: $slug } } }) {
challenge {
forumTopicId
title
description
@ -231,4 +240,5 @@ export const query = graphql`
}
}
}
}
`;

View File

@ -97,7 +97,9 @@ class ShowVideo extends Component<ShowVideoProps, ShowVideoState> {
const {
challengeMounted,
data: {
challengeNode: { title, challengeType, helpCategory }
challengeNode: {
challenge: { title, challengeType, helpCategory }
}
},
pageContext: { challengeMeta },
updateChallengeMeta
@ -115,13 +117,17 @@ class ShowVideo extends Component<ShowVideoProps, ShowVideoState> {
componentDidUpdate(prevProps: ShowVideoProps): void {
const {
data: {
challengeNode: { title: prevTitle }
challengeNode: {
challenge: { title: prevTitle }
}
}
} = prevProps;
const {
challengeMounted,
data: {
challengeNode: { title: currentTitle, challengeType, helpCategory }
challengeNode: {
challenge: { title: currentTitle, challengeType, helpCategory }
}
},
pageContext: { challengeMeta },
updateChallengeMeta
@ -169,6 +175,7 @@ class ShowVideo extends Component<ShowVideoProps, ShowVideoState> {
const {
data: {
challengeNode: {
challenge: {
fields: { blockName },
title,
description,
@ -180,6 +187,7 @@ class ShowVideo extends Component<ShowVideoProps, ShowVideoState> {
bilibiliIds,
question: { text, answers, solution }
}
}
},
openCompletionModal,
pageContext: {
@ -313,7 +321,8 @@ export default connect(
export const query = graphql`
query VideoChallenge($slug: String!) {
challengeNode(fields: { slug: { eq: $slug } }) {
challengeNode(challenge: { fields: { slug: { eq: $slug } } }) {
challenge {
videoId
videoLocaleIds {
espanol
@ -343,4 +352,5 @@ export const query = graphql`
translationPending
}
}
}
`;

View File

@ -101,7 +101,7 @@ export class Block extends Component<BlockProps> {
} = this.props;
let completedCount = 0;
const challengesWithCompleted = challenges.map(challenge => {
const challengesWithCompleted = challenges.map(({ challenge }) => {
const { id } = challenge;
const isCompleted = completedChallengeIds.some(
(completedChallengeId: string) => completedChallengeId === id
@ -112,7 +112,7 @@ export class Block extends Component<BlockProps> {
return { ...challenge, isCompleted };
});
const isProjectBlock = challenges.some(challenge => {
const isProjectBlock = challenges.some(({ challenge }) => {
const isJsProject =
challenge.order === 10 && challenge.challengeType === 5;

View File

@ -21,7 +21,7 @@ function renderMenuItems({
edges?: Array<{ node: ChallengeNode }>;
}) {
return edges
.map(({ node }) => node)
.map(({ node: { challenge } }) => challenge)
.map(({ title, fields: { slug } }) => (
<Link key={'intro-' + slug} to={slug}>
<ListGroupItem>{title}</ListGroupItem>
@ -42,7 +42,8 @@ function IntroductionPage({
html,
frontmatter: { block }
} = markdownRemark;
const firstLesson = allChallengeNode && allChallengeNode.edges[0].node;
const firstLesson =
allChallengeNode && allChallengeNode.edges[0].node.challenge;
const firstLessonPath = firstLesson
? firstLesson.fields.slug
: '/strange-place';
@ -97,11 +98,18 @@ export const query = graphql`
html
}
allChallengeNode(
filter: { block: { eq: $block } }
sort: { fields: [superOrder, order, challengeOrder] }
filter: { challenge: { block: { eq: $block } } }
sort: {
fields: [
challenge___superOrder
challenge___order
challenge___challengeOrder
]
}
) {
edges {
node {
challenge {
fields {
slug
}
@ -110,4 +118,5 @@ export const query = graphql`
}
}
}
}
`;

View File

@ -141,15 +141,15 @@ const SuperBlockIntroductionPage = (props: SuperBlockProp) => {
if (isSignedIn) {
// see if currentChallenge is in this superBlock
const currentChallengeEdge = edges.find(
edge => edge.node.id === currentChallengeId
edge => edge.node.challenge.id === currentChallengeId
);
return currentChallengeEdge
? currentChallengeEdge.node.block
: edge.node.block;
? currentChallengeEdge.node.challenge.block
: edge.node.challenge.block;
}
return edge.node.block;
return edge.node.challenge.block;
};
const initializeExpandedState = () => {
@ -173,7 +173,9 @@ const SuperBlockIntroductionPage = (props: SuperBlockProp) => {
} = props;
const nodesForSuperBlock = edges.map(({ node }) => node);
const blockDashedNames = uniq(nodesForSuperBlock.map(({ block }) => block));
const blockDashedNames = uniq(
nodesForSuperBlock.map(({ challenge: { block } }) => block)
);
const i18nSuperBlock = t(`intro:${superBlock}.title`);
const i18nTitle =
superBlock === SuperBlocks.CodingInterviewPrep
@ -206,7 +208,7 @@ const SuperBlockIntroductionPage = (props: SuperBlockProp) => {
<Block
blockDashedName={blockDashedName}
challenges={nodesForSuperBlock.filter(
node => node.block === blockDashedName
node => node.challenge.block === blockDashedName
)}
superBlock={superBlock}
/>
@ -263,11 +265,18 @@ export const query = graphql`
}
}
allChallengeNode(
sort: { fields: [superOrder, order, challengeOrder] }
filter: { superBlock: { eq: $superBlock } }
sort: {
fields: [
challenge___superOrder
challenge___order
challenge___challengeOrder
]
}
filter: { challenge: { superBlock: { eq: $superBlock } } }
) {
edges {
node {
challenge {
fields {
slug
blockName
@ -283,4 +292,5 @@ export const query = graphql`
}
}
}
}
`;

View File

@ -45,12 +45,12 @@ const views = {
function getNextChallengePath(_node, index, nodeArray) {
const next = nodeArray[index + 1];
return next ? next.node.fields.slug : '/learn';
return next ? next.node.challenge.fields.slug : '/learn';
}
function getPrevChallengePath(_node, index, nodeArray) {
const prev = nodeArray[index - 1];
return prev ? prev.node.fields.slug : '/learn';
return prev ? prev.node.challenge.fields.slug : '/learn';
}
function getTemplateComponent(challengeType) {
@ -58,7 +58,7 @@ function getTemplateComponent(challengeType) {
}
exports.createChallengePages = function (createPage) {
return function ({ node: challenge }, index, allChallengeEdges) {
return function ({ node: { challenge } }, index, allChallengeEdges) {
const {
superBlock,
block,
@ -103,8 +103,8 @@ function getProjectPreviewConfig(challenge, allChallengeEdges) {
const { block, challengeOrder, usesMultifileEditor } = challenge;
const challengesInBlock = allChallengeEdges
.filter(({ node }) => node.block === block)
.map(({ node }) => node);
.filter(({ node: { challenge } }) => challenge.block === block)
.map(({ node: { challenge } }) => challenge);
const lastChallenge = challengesInBlock[challengesInBlock.length - 1];
const solutionToLastChallenge = sortChallengeFiles(
lastChallenge.solutions[0] ?? []