diff --git a/client/src/templates/Challenges/backend/Show.js b/client/src/templates/Challenges/backend/Show.js index cafbf692b9..4d52696700 100644 --- a/client/src/templates/Challenges/backend/Show.js +++ b/client/src/templates/Challenges/backend/Show.js @@ -164,6 +164,9 @@ export class BackEnd extends Component { } }, output, + pageContext: { + challengeMeta: { introPath, nextChallengePath, prevChallengePath } + }, tests, isSignedIn, executeChallenge, @@ -181,7 +184,14 @@ export class BackEnd extends Component { - {blockNameTitle} + + {blockNameTitle} + - {children || 'Happy Coding!'} - {icon} - +
+ {showPrevNextBtns ? ( + + < + + ) : null} +

+ {children || 'Happy Coding!'} + {icon} +

+ {showPrevNextBtns ? ( + + > + + ) : null} +
); } diff --git a/client/src/templates/Challenges/components/ChallengeTitle.test.js b/client/src/templates/Challenges/components/ChallengeTitle.test.js new file mode 100644 index 0000000000..1a6b295cf9 --- /dev/null +++ b/client/src/templates/Challenges/components/ChallengeTitle.test.js @@ -0,0 +1,73 @@ +/* global expect */ + +import React from 'react'; +import Enzyme, { shallow } from 'enzyme'; +import Adapter from 'enzyme-adapter-react-16'; +import Link from '../../../components/helpers/Link'; +import renderer from 'react-test-renderer'; + +import ChallengeTitle from './Challenge-Title'; + +Enzyme.configure({ adapter: new Adapter() }); + +const baseProps = { + children: 'title text', + introPath: '/intro/path', + isCompleted: true, + nextChallengePath: '/next', + prevChallengePath: '/prev', + showPrevNextBtns: true +}; + +describe('', () => { + it('renders 0 s by default', () => { + const titleToRender = ; + const title = shallow(titleToRender); + expect(title.find(Link).length).toBe(0); + }); + + it('renders a previous and a next ', () => { + const titleToRender = ; + const title = shallow(titleToRender); + expect(title.find(Link).length).toBe(2); + }); + + it('has a link to the previous challenge', () => { + const titleToRender = ; + const title = shallow(titleToRender); + expect( + title + .find(Link) + .first() + .prop('to') + ).toBe('/prev'); + }); + + it('has a link to the next introduction if there is one', () => { + const titleToRender = ; + const title = shallow(titleToRender); + expect( + title + .find(Link) + .last() + .prop('to') + ).toBe('/intro/path'); + }); + + it('has a link to the next challenge otherwise', () => { + const props = { ...baseProps, introPath: null }; + const titleToRender = ; + const title = shallow(titleToRender); + expect( + title + .find(Link) + .last() + .prop('to') + ).toBe('/next'); + }); + + it('renders correctly', () => { + const tree = renderer.create().toJSON(); + expect(tree).toMatchSnapshot(); + }); +}); diff --git a/client/src/templates/Challenges/components/Side-Panel.js b/client/src/templates/Challenges/components/Side-Panel.js index 1e7684ca1e..1adfca2ee6 100644 --- a/client/src/templates/Challenges/components/Side-Panel.js +++ b/client/src/templates/Challenges/components/Side-Panel.js @@ -35,7 +35,11 @@ const propTypes = { guideUrl: PropTypes.string, initConsole: PropTypes.func.isRequired, instructions: PropTypes.string, + introPath: PropTypes.string, + nextChallengePath: PropTypes.string, + prevChallengePath: PropTypes.string, section: PropTypes.string, + showPrevNextBtns: PropTypes.bool, showToolPanel: PropTypes.bool, tests: PropTypes.arrayOf(PropTypes.object), title: PropTypes.string, @@ -64,9 +68,13 @@ export class SidePanel extends Component { title, description, instructions, + introPath, guideUrl, + nextChallengePath, + prevChallengePath, tests, section, + showPrevNextBtns, showToolPanel, videoUrl } = this.props; @@ -74,7 +82,14 @@ export class SidePanel extends Component {
- {title} + + {title} + renders correctly 1`] = ` +
+ + < + +

+ title text + +

+ + > + +
+`; diff --git a/client/src/templates/Challenges/components/challenge-title.css b/client/src/templates/Challenges/components/challenge-title.css new file mode 100644 index 0000000000..8ddd0df210 --- /dev/null +++ b/client/src/templates/Challenges/components/challenge-title.css @@ -0,0 +1,14 @@ +.challenge-title-wrap { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 1.45rem; +} + +.challenge-title { + padding: 0 15px; +} + +.challenge-title-wrap > a { + align-self: flex-start; +} diff --git a/client/src/templates/Challenges/project/Show.js b/client/src/templates/Challenges/project/Show.js index 7eb883c9e5..0e1466457c 100644 --- a/client/src/templates/Challenges/project/Show.js +++ b/client/src/templates/Challenges/project/Show.js @@ -100,6 +100,9 @@ export class Project extends Component { } }, openCompletionModal, + pageContext: { + challengeMeta: { introPath, nextChallengePath, prevChallengePath } + }, updateProjectFormValues } = this.props; const isFrontEnd = challengeType === frontEndProject; @@ -113,6 +116,10 @@ export class Project extends Component { className='full-height' description={description} guideUrl={guideUrl} + introPath={introPath} + nextChallengePath={nextChallengePath} + prevChallengePath={prevChallengePath} + showPrevNextBtns={true} title={blockNameTitle} /> - {title} + + {title} +
); diff --git a/client/src/templates/Challenges/redux/index.js b/client/src/templates/Challenges/redux/index.js index 194e2f4e4b..7d427d0085 100644 --- a/client/src/templates/Challenges/redux/index.js +++ b/client/src/templates/Challenges/redux/index.js @@ -25,6 +25,7 @@ const initialState = { challengeMeta: { id: '', nextChallengePath: '/', + prevChallengePath: '/', introPath: '', challengeType: -1 }, diff --git a/client/utils/gatsby/challengePageCreator.js b/client/utils/gatsby/challengePageCreator.js index 203e68feb2..91a981ec95 100644 --- a/client/utils/gatsby/challengePageCreator.js +++ b/client/utils/gatsby/challengePageCreator.js @@ -34,8 +34,14 @@ const views = { const getNextChallengePath = (node, index, nodeArray) => { const next = nodeArray[index + 1]; - return next ? next.node.fields.slug : '/'; + return next ? next.node.fields.slug : '/learn'; }; + +const getPrevChallengePath = (node, index, nodeArray) => { + const prev = nodeArray[index - 1]; + return prev ? prev.node.fields.slug : '/learn'; +}; + const getTemplateComponent = challengeType => views[viewTypes[challengeType]]; const getIntroIfRequired = (node, index, nodeArray) => { @@ -74,6 +80,7 @@ exports.createChallengePages = createPage => ({ node }, index, thisArray) => { template, required, nextChallengePath: getNextChallengePath(node, index, thisArray), + prevChallengePath: getPrevChallengePath(node, index, thisArray), id }, slug