Skip to content

Commit

Permalink
feat: gather steps inside tutorial sections
Browse files Browse the repository at this point in the history
jerelmiller committed Oct 14, 2020
1 parent 13df919 commit f96033c
Showing 4 changed files with 95 additions and 54 deletions.
106 changes: 66 additions & 40 deletions src/components/Tutorial.js
Original file line number Diff line number Diff line change
@@ -7,59 +7,85 @@ import { diffLines } from 'diff';
const Tutorial = ({ children }) => {
children = Children.toArray(children);

const initialState =
const initialProjectState =
children[0].props.mdxType === 'Project'
? parseFileInfoFromConfig(children[0])
: parseFileInfoFromChildren(children);
? parseProjectStateFromConfig(children[0])
: parseProjectStateFromChildren(children);

return children
.filter((child) => child.props.mdxType === 'TutorialStep')
.reduce((steps, stepElement, idx, arr) => {
const { elements } = children.reduce(
(memo, child) => {
const { elements, currentProjectState } = memo;

if (child.props.mdxType === 'Project') {
return memo;
} else if (child.props.mdxType === 'TutorialSection') {
const { steps, currentProjectState: projectState } = gatherSteps(
child,
currentProjectState
);

return {
currentProjectState: projectState,
elements: [...elements, cloneElement(child, { children: steps })],
};
}

return { elements: [...elements, child], currentProjectState };
},
{ elements: [], currentProjectState: initialProjectState }
);

return elements;
};

Tutorial.propTypes = {
children: PropTypes.node,
};

const gatherSteps = (parentElement, initialProjectState) => {
return Children.toArray(parentElement.props.children).reduce(
({ steps, currentProjectState }, stepElement, idx, children) => {
const sharedProps = { stepNumber: idx + 1, totalSteps: children.length };
const codeBlock = Children.toArray(stepElement.props.children).find(
(child) => isCodeBlock(child) && !isShellCommand(child)
);

if (!codeBlock) {
return [
...steps,
cloneElement(stepElement, { index: idx, totalSteps: arr.length }),
];
return {
currentProjectState,
steps: [...steps, cloneElement(stepElement, sharedProps)],
};
}

const previousStep = new Map(
steps
.slice(0, idx)
.map((element) => element.props.step)
.reverse()
.find(Boolean) || initialState
);

const props = parseCodeBlockProps(codeBlock);
const { code: prevCode } = previousStep.get(props.fileName);

return [
...steps,
cloneElement(stepElement, {
codeBlock: { ...props, diff: diffLines(prevCode, props.code) },
step: previousStep.set(props.fileName, {
code: props.code,
language: props.language,
const { code: prevCode } = currentProjectState.get(props.fileName);
const projectState = clone(currentProjectState).set(props.fileName, {
code: props.code,
language: props.language,
});

return {
currentProjectState: projectState,
steps: [
...steps,
cloneElement(stepElement, {
...sharedProps,
codeBlock: { ...props, diff: diffLines(prevCode, props.code) },
project: projectState,
children: Children.toArray(stepElement.props.children).filter(
(child) => isShellCommand(child) || !isCodeBlock(child)
),
}),
index: idx,
totalSteps: arr.length,
children: Children.toArray(stepElement.props.children).filter(
(child) => isShellCommand(child) || !isCodeBlock(child)
),
}),
];
}, []);
],
};
},
{ steps: [], currentProjectState: initialProjectState }
);
};

Tutorial.propTypes = {
children: PropTypes.node,
};
const clone = (map) => new Map(map);

const parseFileInfoFromConfig = (configElement) => {
const parseProjectStateFromConfig = (configElement) => {
return Children.toArray(configElement.props.children)
.filter((child) => isCodeBlock(child) && !isShellCommand(child))
.reduce((map, child) => {
@@ -69,7 +95,7 @@ const parseFileInfoFromConfig = (configElement) => {
}, new Map());
};

const parseFileInfoFromChildren = (children) => {
const parseProjectStateFromChildren = (children) => {
return children
.flatMap((child) => {
switch (child.props.mdxType) {
8 changes: 4 additions & 4 deletions src/components/TutorialEditor.js
Original file line number Diff line number Diff line change
@@ -43,7 +43,7 @@ const diffHighlightedLines = (diff) => {
return highlightedLines.join(',');
};

const TutorialEditor = ({ codeBlock, files }) => {
const TutorialEditor = ({ codeBlock, project }) => {
const [selectedFile, setSelectedFile] = useState(codeBlock.fileName);

return (
@@ -56,7 +56,7 @@ const TutorialEditor = ({ codeBlock, files }) => {
border-top-right-radius: 0.25rem;
`}
>
{Array.from(files.keys()).map((fileName) => (
{Array.from(project.keys()).map((fileName) => (
<button
key={fileName}
type="button"
@@ -78,7 +78,7 @@ const TutorialEditor = ({ codeBlock, files }) => {
))}
</div>

{Array.from(files.entries()).map(([fileName, { code, language }]) => (
{Array.from(project.entries()).map(([fileName, { code, language }]) => (
<CodeBlock
key={fileName}
lineNumbers
@@ -107,7 +107,7 @@ const TutorialEditor = ({ codeBlock, files }) => {

TutorialEditor.propTypes = {
codeBlock: PropTypes.object.isRequired,
files: PropTypes.instanceOf(Map).isRequired,
project: PropTypes.instanceOf(Map).isRequired,
};

export default TutorialEditor;
21 changes: 18 additions & 3 deletions src/components/TutorialSection.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
// Does not render. Will be used by the `Tutorial` component
const TutorialSection = () => {
return null;
import React from 'react';
import PropTypes from 'prop-types';
import { css } from '@emotion/core';

const TutorialSection = ({ children }) => {
return (
<section
css={css`
margin-bottom: 4rem;
`}
>
{children}
</section>
);
};

TutorialSection.propTypes = {
children: PropTypes.node,
};

export default TutorialSection;
14 changes: 7 additions & 7 deletions src/components/TutorialStep.js
Original file line number Diff line number Diff line change
@@ -7,8 +7,8 @@ import Markdown from './Markdown';
const TutorialStep = ({
children,
codeBlock,
step,
index,
project,
stepNumber,
title,
totalSteps,
}) => {
@@ -27,7 +27,7 @@ const TutorialStep = ({
margin-bottom: 0;
`}
>
Step {index + 1} of {totalSteps}
Step {stepNumber} of {totalSteps}
</p>
<h3
css={css`
@@ -38,7 +38,7 @@ const TutorialStep = ({
>
<Markdown source={title} />
</h3>
{step ? (
{project ? (
<div
css={css`
display: grid;
@@ -47,7 +47,7 @@ const TutorialStep = ({
`}
>
<div>{children}</div>
<TutorialEditor codeBlock={codeBlock} files={step} />
<TutorialEditor codeBlock={codeBlock} project={project} />
</div>
) : (
children
@@ -59,8 +59,8 @@ const TutorialStep = ({
TutorialStep.propTypes = {
children: PropTypes.node,
codeBlock: PropTypes.object.isRequired,
index: PropTypes.number.isRequired,
step: PropTypes.instanceOf(Map).isRequired,
stepNumber: PropTypes.number.isRequired,
project: PropTypes.instanceOf(Map).isRequired,
title: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
totalSteps: PropTypes.number.isRequired,
};

0 comments on commit f96033c

Please sign in to comment.