Feat: News in the client app (#34392)

This commit is contained in:
Stuart Taylor
2018-11-29 12:12:15 +00:00
committed by Valeriy
parent 28798dc008
commit d327a5c36b
87 changed files with 2334 additions and 1403 deletions

View File

@@ -0,0 +1,114 @@
const path = require('path');
const { dasherize } = require('..');
const { viewTypes } = require('../challengeTypes');
const backend = path.resolve(
__dirname,
'../../src/templates/Challenges/backend/Show.js'
);
const classic = path.resolve(
__dirname,
'../../src/templates/Challenges/classic/Show.js'
);
const project = path.resolve(
__dirname,
'../../src/templates/Challenges/project/Show.js'
);
const intro = path.resolve(
__dirname,
'../../src/templates/Introduction/Intro.js'
);
const superBlockIntro = path.resolve(
__dirname,
'../../src/templates/Introduction/SuperBlockIntro.js'
);
const views = {
backend,
classic,
modern: classic,
project
// quiz: Quiz
};
const getNextChallengePath = (node, index, nodeArray) => {
const next = nodeArray[index + 1];
return next ? next.node.fields.slug : '/';
};
const getTemplateComponent = challengeType => views[viewTypes[challengeType]];
const getIntroIfRequired = (node, index, nodeArray) => {
const next = nodeArray[index + 1];
const isEndOfBlock = next && next.node.challengeOrder === 0;
let nextSuperBlock = '';
let nextBlock = '';
if (next) {
const { superBlock, block } = next.node;
nextSuperBlock = superBlock;
nextBlock = block;
}
return isEndOfBlock
? `/learn/${dasherize(nextSuperBlock)}/${dasherize(nextBlock)}`
: '';
};
exports.createChallengePages = createPage => ({ node }, index, thisArray) => {
const {
fields: { slug },
required = [],
template,
challengeType,
id
} = node;
if (challengeType === 7) {
return null;
}
return createPage({
path: slug,
component: getTemplateComponent(challengeType),
context: {
challengeMeta: {
introPath: getIntroIfRequired(node, index, thisArray),
template,
required,
nextChallengePath: getNextChallengePath(node, index, thisArray),
id
},
slug
}
});
};
exports.createBlockIntroPages = createPage => edge => {
const {
fields: { slug },
frontmatter: { block }
} = edge.node;
return createPage({
path: slug,
component: intro,
context: {
block: dasherize(block),
slug
}
});
};
exports.createSuperBlockIntroPages = createPage => edge => {
const {
fields: { slug },
frontmatter: { superBlock }
} = edge.node;
return createPage({
path: slug,
component: superBlockIntro,
context: {
superBlock: dasherize(superBlock),
slug
}
});
};

View File

@@ -0,0 +1,41 @@
const path = require('path');
const select = require('unist-util-select');
const { head } = require('lodash');
const { isAStubRE } = require('../regEx');
const guideArticle = path.resolve(
__dirname,
'../../src/templates/Guide/GuideArticle.js'
);
exports.createGuideArticlePages = createPage => ({
node: {
htmlAst,
excerpt,
fields: { slug },
id
}
}) => {
let meta = {};
if (!isAStubRE.test(excerpt)) {
const featureImage = head(select(htmlAst, 'element[tagName=img]'));
meta.featureImage = featureImage
? featureImage.properties.src
: 'https://s3.amazonaws.com/freecodecamp' +
'/reecodecamp-square-logo-large.jpg';
const description = head(select(htmlAst, 'element[tagName=p]'));
meta.description = description ? description.children[0].value : '';
}
return createPage({
path: `/guide${slug}`,
component: guideArticle,
context: {
id,
meta
}
});
};

View File

@@ -1,154 +1,9 @@
const path = require('path');
const select = require('unist-util-select');
const { head } = require('lodash');
const challengePageCreators = require('./challengePageCreator');
const guidePageCreators = require('./guidePageCreator');
const newsPageCreators = require('./newsPageCreator');
const { dasherize } = require('..');
const { isAStubRE } = require('../regEx');
const { viewTypes } = require('../challengeTypes');
const backend = path.resolve(
__dirname,
'../../src/templates/Challenges/backend/Show.js'
);
const classic = path.resolve(
__dirname,
'../../src/templates/Challenges/classic/Show.js'
);
const project = path.resolve(
__dirname,
'../../src/templates/Challenges/project/Show.js'
);
const intro = path.resolve(
__dirname,
'../../src/templates/Introduction/Intro.js'
);
const superBlockIntro = path.resolve(
__dirname,
'../../src/templates/Introduction/SuperBlockIntro.js'
);
const guideArticle = path.resolve(
__dirname,
'../../src/templates/Guide/GuideArticle.js'
);
const views = {
backend,
classic,
modern: classic,
project
// quiz: Quiz
};
const getNextChallengePath = (node, index, nodeArray) => {
const next = nodeArray[index + 1];
return next ? next.node.fields.slug : '/';
};
const getTemplateComponent = challengeType => views[viewTypes[challengeType]];
const getIntroIfRequired = (node, index, nodeArray) => {
const next = nodeArray[index + 1];
const isEndOfBlock = next && next.node.challengeOrder === 0;
let nextSuperBlock = '';
let nextBlock = '';
if (next) {
const { superBlock, block } = next.node;
nextSuperBlock = superBlock;
nextBlock = block;
}
return isEndOfBlock
? `/learn/${dasherize(nextSuperBlock)}/${dasherize(nextBlock)}`
: '';
};
exports.createChallengePages = createPage => ({ node }, index, thisArray) => {
const {
fields: { slug },
required = [],
template,
challengeType,
id
} = node;
if (challengeType === 7) {
return null;
}
return createPage({
path: slug,
component: getTemplateComponent(challengeType),
context: {
challengeMeta: {
introPath: getIntroIfRequired(node, index, thisArray),
template,
required,
nextChallengePath: getNextChallengePath(node, index, thisArray),
id
},
slug
}
});
};
exports.createBlockIntroPages = createPage => edge => {
const {
fields: { slug },
frontmatter: { block }
} = edge.node;
return createPage({
path: slug,
component: intro,
context: {
block: dasherize(block),
slug
}
});
};
exports.createSuperBlockIntroPages = createPage => edge => {
const {
fields: { slug },
frontmatter: { superBlock }
} = edge.node;
return createPage({
path: slug,
component: superBlockIntro,
context: {
superBlock: dasherize(superBlock),
slug
}
});
};
exports.createGuideArticlePages = createPage => ({
node: {
htmlAst,
excerpt,
fields: { slug },
id
}
}) => {
let meta = {};
if (!isAStubRE.test(excerpt)) {
const featureImage = head(select(htmlAst, 'element[tagName=img]'));
meta.featureImage = featureImage
? featureImage.properties.src
: 'https://s3.amazonaws.com/freecodecamp' +
'/reecodecamp-square-logo-large.jpg';
const description = head(select(htmlAst, 'element[tagName=p]'));
meta.description = description ? description.children[0].value : '';
}
return createPage({
path: `/guide${slug}`,
component: guideArticle,
context: {
id,
meta
}
});
module.exports = {
...challengePageCreators,
...guidePageCreators,
...newsPageCreators
};

View File

@@ -0,0 +1,22 @@
const path = require('path');
const newsArticle = path.resolve(
__dirname, '../../src/templates/News/ShowArticle/index.js'
);
exports.createNewsArticle = createPage => ({
node: {
fields: { slug },
shortId,
id
}
}) =>
createPage({
path: slug,
component: newsArticle,
context: {
slug,
shortId,
id
}
});

18
client/utils/news.js Normal file
View File

@@ -0,0 +1,18 @@
exports.createArticleSlug = ({
username = '',
slugPart = '',
shortId = ''
} = {}) => {
if (!username || !slugPart || !shortId) {
throw new Error(`
createArtcileSlug: One or more properties were missing, all are required
{
username: ${username},
slugPart: ${slugPart},
shortId: ${shortId}
}
`);
}
return `/news/${username}/${slugPart.concat('--', shortId)}`;
};

32
client/utils/news.test.js Normal file
View File

@@ -0,0 +1,32 @@
/* global describe it expect */
import { mockArguments, slugWithId} from '../src/__mocks__/news-article';
import { createArticleSlug } from './news';
describe('news utils', () => {
describe('createArticleSlug', () => {
it('returns a string', () => {
expect(typeof createArticleSlug(mockArguments)).toEqual('string');
});
it('throws when values are missing', () => {
expect.assertions(3);
/* eslint-disable no-undefined */
expect(() =>
createArticleSlug({ ...mockArguments, shortId: undefined })
).toThrow();
expect(() =>
createArticleSlug({ ...mockArguments, slugPart: undefined })
).toThrow();
expect(() =>
createArticleSlug({ ...mockArguments, username: undefined })
).toThrow();
});
it('creates a slug in the expected format', () => {
const result = createArticleSlug(mockArguments);
expect(result).toEqual(slugWithId);
});
});
});