Skip to content

Commit 3ff9078

Browse files
Merge pull request #36 from howtographql/progress-bar-tutorials
Progress bar tutorials
2 parents 044e72e + bb9303d commit 3ff9078

File tree

13 files changed

+213
-108
lines changed

13 files changed

+213
-108
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import * as React from 'react';
2+
import { Text } from '../shared/base';
3+
import { percent } from '../../utils/helpers';
4+
5+
const ProgressBanner = ({ tutorial }) => {
6+
let currentChapter = tutorial.viewerUserTutorial.currentChapter;
7+
let numberofChapters = tutorial.numberofChapters;
8+
let percentage = currentChapter
9+
? percent(numberofChapters, currentChapter)
10+
: 0;
11+
return (
12+
<div>
13+
{percentage ? <Text>{percentage}%</Text> : <Text> No Progress </Text>}
14+
</div>
15+
);
16+
};
17+
18+
export default ProgressBanner;

packages/gatsby-theme/src/components/community/TutorialListing.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { Link } from 'gatsby';
55
import { Query } from 'react-apollo';
66
import UpvoteMutation from './UpvoteMutation';
77
import BookmarkMutation from './BookmarkMutation';
8-
import Percentage from '../shared/Percentage';
8+
import ProgressBanner from './ProgressBanner';
99
import { getTutorialbyGatsbyID } from '../../utils/queries';
1010

1111
type TutorialListingProps = {
@@ -47,7 +47,7 @@ const TutorialListing: React.FunctionComponent<TutorialListingProps> = ({
4747
<div>
4848
<UpvoteMutation tutorial={data.getTutorialbyGatsbyID} />
4949
<BookmarkMutation tutorial={data.getTutorialbyGatsbyID} />
50-
<Percentage tutorial={data.getTutorialbyGatsbyID} />
50+
<ProgressBanner tutorial={data.getTutorialbyGatsbyID} />
5151
</div>
5252
)}
5353
</Box>

packages/gatsby-theme/src/components/courses/CourseCard.tsx

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,24 @@
11
import * as React from 'react';
22
import { Card, Flex, Image } from '../shared/base';
3+
import { Query } from 'react-apollo';
34
import ProgressBar from '../shared/ProgressBar';
45
import { TutorialButton } from '../shared/buttons';
6+
import { getTutorialbyGatsbyID } from '../../utils/queries';
57
import { getTutorialOverviewSlug } from '../../utils/getTutorialSlug';
8+
import { optionalChaining, percent } from '../../utils/helpers';
69

710
type CourseCardProps = {
811
tutorialTitle: string;
912
fileAbsolutePath: string;
13+
gatsbyID: string;
1014
};
1115

1216
const CourseCard: React.FunctionComponent<CourseCardProps> = ({
17+
gatsbyID,
1318
tutorialTitle,
1419
fileAbsolutePath,
1520
}) => {
21+
let path = getTutorialOverviewSlug(fileAbsolutePath);
1622
return (
1723
<Card m={[1, 1, 1]} p={[2, 2, 2]}>
1824
<Flex flexDirection="column" alignItems="center" justifyContent="center">
@@ -21,10 +27,35 @@ const CourseCard: React.FunctionComponent<CourseCardProps> = ({
2127
src="https://i.ibb.co/TcKwmwR/Icons.png"
2228
/>
2329
<h3>{tutorialTitle}</h3>
24-
<ProgressBar percentage={Math.floor(Math.random() * 100)} width={80} />
25-
<a href={getTutorialOverviewSlug(fileAbsolutePath)}>
26-
<TutorialButton>Start Tutorial</TutorialButton>
27-
</a>
30+
<Query query={getTutorialbyGatsbyID} variables={{ gatsbyID: gatsbyID }}>
31+
{({ data }) => {
32+
let buttonText = 'Start Tutorial';
33+
let percentage = 0;
34+
if (
35+
optionalChaining(
36+
() =>
37+
data.getTutorialbyGatsbyID.viewerUserTutorial.currentChapter,
38+
)
39+
) {
40+
percentage = percent(
41+
data.getTutorialbyGatsbyID.numberofChapters,
42+
data.getTutorialbyGatsbyID.viewerUserTutorial.currentChapter,
43+
);
44+
buttonText = 'Continue Tutorial';
45+
if (percentage === 100) {
46+
buttonText = 'Take Again';
47+
}
48+
}
49+
return (
50+
<div>
51+
{!!percentage && <ProgressBar percentage={percentage} />}
52+
<a href={path}>
53+
<TutorialButton>{buttonText}</TutorialButton>
54+
</a>
55+
</div>
56+
);
57+
}}
58+
</Query>
2859
</Flex>
2960
</Card>
3061
);

packages/gatsby-theme/src/components/courses/CourseSection.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ type Node = {
1919
};
2020

2121
type Frontmatter = {
22+
id: string;
2223
tutorialTitle: string;
2324
description: string;
2425
};
@@ -39,6 +40,7 @@ const CourseSection: React.FunctionComponent<CourseSectionProps> = ({
3940
{data.map(tutorial => (
4041
<Box width={[1, 0.8, 0.4]} key={tutorial.node.id}>
4142
<CourseCard
43+
gatsbyID={tutorial.node.frontmatter.id}
4244
tutorialTitle={tutorial.node.frontmatter.tutorialTitle}
4345
fileAbsolutePath={tutorial.node.fileAbsolutePath}
4446
/>

packages/gatsby-theme/src/components/shared/Percentage.tsx

Lines changed: 0 additions & 19 deletions
This file was deleted.

packages/gatsby-theme/src/components/shared/ProgressBar.tsx

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,23 @@
11
import * as React from 'react';
22
import { styled } from '../../styles';
33
import { Text } from './base';
4+
import {percent} from "../../utils/helpers"
45

5-
interface ProgressBarProps extends FillerProps, ContainerProps {}
6+
interface ProgressBarProps extends FillerProps {
7+
numberofChapters?: number;
8+
currentChapter?: number;
9+
}
610

711
const ProgressBar: React.FunctionComponent<ProgressBarProps> = ({
812
percentage,
9-
width,
13+
numberofChapters,
14+
currentChapter,
1015
}) => {
16+
if (!percentage && numberofChapters && currentChapter) {
17+
percentage = percent(currentChapter, numberofChapters)
18+
}
1119
return (
12-
<Container width={width}>
20+
<Container>
1321
<Outside>
1422
<Filler percentage={percentage} />
1523
</Outside>
@@ -20,12 +28,8 @@ const ProgressBar: React.FunctionComponent<ProgressBarProps> = ({
2028
);
2129
};
2230

23-
interface ContainerProps {
24-
width?: number;
25-
}
26-
27-
const Container = styled.div<ContainerProps>`
28-
width: ${props => props.width || '100'}%;
31+
const Container = styled.div`
32+
width: 100%;
2933
`;
3034

3135
const Outside = styled.div`
@@ -38,7 +42,7 @@ const Outside = styled.div`
3842
`;
3943

4044
interface FillerProps {
41-
percentage: number;
45+
percentage?: number;
4246
}
4347

4448
const Filler = styled.div<FillerProps>`

packages/gatsby-theme/src/components/templates/Tutorial.tsx

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { HideOnTablet, ShowOnTablet } from '../../utils/responsive';
99
import { Flex, Box } from '../shared/base';
1010
import { optionalChaining } from '../../utils/helpers';
1111
import ChapterMutation from '../tutorial/ChapterMutation';
12+
import { getTutorialSlug } from '../../utils/getTutorialSlug';
1213

1314
type TutorialLayoutProps = { data: TutorialMdxQuery } & RouterProps;
1415

@@ -19,20 +20,43 @@ const TutorialLayout: React.FunctionComponent<TutorialLayoutProps> = ({
1920
if (!data) {
2021
return null;
2122
}
22-
const { pageTitle } = data!.mdx!.frontmatter!;
23-
const gatsbyID = optionalChaining(
24-
() => data!.tutorialTitle!.frontmatter!.id!,
25-
);
2623

24+
//title of the chapter
25+
const { pageTitle } = optionalChaining(() => data.mdx.frontmatter);
26+
//title of the tutorial
2727
const tutorialTitle = optionalChaining(
28-
() => data!.tutorialTitle!.frontmatter!.tutorialTitle!,
28+
() => data.tutorialTitle.frontmatter.tutorialTitle,
29+
);
30+
//gatsbyID to fetch user information about tutorial
31+
const gatsbyID = optionalChaining(() => data.tutorialTitle.frontmatter.id);
32+
//all chapters in this tutorial
33+
const chapters = optionalChaining(() =>
34+
data.pageTitles.edges.map(a => {
35+
return {
36+
chapterTitle: optionalChaining(() => a.node.frontmatter.pageTitle),
37+
chapterPath: getTutorialSlug(
38+
optionalChaining(() => a.node.fileAbsolutePath),
39+
),
40+
};
41+
}),
2942
);
30-
const chapters =
31-
optionalChaining(() =>
32-
data!.pageTitles!.edges!.map(a => a.node!.frontmatter!.pageTitle!),
33-
) || [];
43+
3444
const { location } = props;
35-
const currentChapter = chapters.indexOf(pageTitle) + 1;
45+
46+
//all titles of chapters in this tutorial
47+
const chapterTitles = chapters.map(chapter => chapter.chapterTitle);
48+
//the number of this current chapter
49+
const currentChapterIndex = chapterTitles.indexOf(pageTitle);
50+
//get the next chapter path
51+
const findNextChapterPath = () => {
52+
if (currentChapterIndex + 1 === chapters.length) {
53+
return null;
54+
} else {
55+
return chapters[currentChapterIndex + 1].chapterPath;
56+
}
57+
};
58+
//evaluate findNextChapterPath to get the next chapter.
59+
const nextChapterPath = findNextChapterPath();
3660

3761
return (
3862
<Layout location={location}>
@@ -60,7 +84,12 @@ const TutorialLayout: React.FunctionComponent<TutorialLayoutProps> = ({
6084
<ShowOnTablet>
6185
<MDXRenderer>{data!.mdx!.code!.body}</MDXRenderer>
6286
</ShowOnTablet>
63-
<ChapterMutation gatsbyID={gatsbyID} currentChapter={currentChapter} />
87+
<a href={nextChapterPath}>
88+
<ChapterMutation
89+
gatsbyID={gatsbyID}
90+
currentChapter={currentChapterIndex + 1}
91+
/>
92+
</a>
6493
</Layout>
6594
);
6695
};
@@ -89,6 +118,7 @@ export const pageQuery = graphql`
89118
edges {
90119
node {
91120
id
121+
fileAbsolutePath
92122
frontmatter {
93123
pageTitle
94124
}

packages/gatsby-theme/src/components/templates/TutorialOverview.tsx

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,29 @@ import React from 'react';
22
import Layout from '../shared/layout';
33
import Chapter from '../tutorial/Chapter';
44
import { TutorialOverviewQuery } from 'src/graphqlTypes';
5-
import AuthorList from '../tutorial/AuthorList';
65
import TutorialHeader from '../tutorial/TutorialHeader';
76
import { Heading, Flex, Box } from '../shared/base';
8-
import ProgressBar from '../shared/ProgressBar';
9-
import {
10-
GithubButton,
11-
SpectrumButton,
12-
TutorialButton,
13-
} from '../shared/buttons';
7+
import AuthorsProgressBox from '../tutorial/AuthorsProgressBox';
148
import { Content } from '../shared/styledHelpers';
15-
import { authors } from '../../utils/sampleData';
169
import { graphql } from 'gatsby';
10+
import { optionalChaining } from '../../utils/helpers';
11+
import { getTutorialSlug } from '../../utils/getTutorialSlug';
1712

1813
interface PageTemplateProps {
1914
data: TutorialOverviewQuery;
2015
}
2116

2217
const PageTemplate: React.FunctionComponent<PageTemplateProps> = ({ data }) => {
18+
let gatsbyID = optionalChaining(() => data.overview.frontmatter.id);
19+
20+
// This is so that the start button can link to the user's current path
21+
// TO DO find a better way to pass in the which chapter the user is currently on
22+
const chapterPaths = optionalChaining(() =>
23+
data.allMdx.edges.map(a =>
24+
getTutorialSlug(optionalChaining(() => a.node.fileAbsolutePath)),
25+
),
26+
);
27+
2328
return (
2429
<Layout>
2530
<Content>
@@ -33,15 +38,10 @@ const PageTemplate: React.FunctionComponent<PageTemplateProps> = ({ data }) => {
3338
/>
3439
</Box>
3540
<Box width={1 / 4} m={3}>
36-
<TutorialButton>Continue Tutorial</TutorialButton>
37-
<Box m={3}>
38-
<ProgressBar percentage={33} width={100} />
39-
</Box>
40-
<Flex>
41-
<GithubButton>Github</GithubButton>
42-
<SpectrumButton>Spectrum</SpectrumButton>
43-
</Flex>
44-
<AuthorList authors={authors} />
41+
<AuthorsProgressBox
42+
gatsbyID={gatsbyID}
43+
chapterPaths={chapterPaths}
44+
/>
4545
</Box>
4646
</Flex>
4747
<div>
@@ -87,6 +87,7 @@ export const query = graphql`
8787
) {
8888
id
8989
frontmatter {
90+
id
9091
tutorialTitle
9192
banner
9293
description

0 commit comments

Comments
 (0)