From 259f01e9909651e72dd955a6e8aa5c678afb46ed Mon Sep 17 00:00:00 2001 From: shmck Date: Sun, 5 Jul 2020 18:13:28 -0700 Subject: [PATCH] fix level stories Signed-off-by: shmck --- typings/tutorial.d.ts | 4 + .../Tutorial/components/ContentMenu.tsx | 8 +- .../containers/Tutorial/components/Level.tsx | 8 +- web-app/stories/Level.stories.tsx | 477 +++--------------- 4 files changed, 92 insertions(+), 405 deletions(-) diff --git a/typings/tutorial.d.ts b/typings/tutorial.d.ts index e43aa884..92d8ddbf 100644 --- a/typings/tutorial.d.ts +++ b/typings/tutorial.d.ts @@ -1,3 +1,5 @@ +import { ProgressStatus } from './index' + export type Maybe = T | null export type TutorialConfig = { @@ -19,6 +21,7 @@ export type Level = { setup?: Maybe /** A set of tasks for users linked to unit tests */ steps: Array + status?: ProgressStatus } /** A level task */ @@ -29,6 +32,7 @@ export type Step = { solution: Maybe hints?: string[] subtasks?: string[] + status?: ProgressStatus } /** A tutorial for use in VSCode CodeRoad */ diff --git a/web-app/src/containers/Tutorial/components/ContentMenu.tsx b/web-app/src/containers/Tutorial/components/ContentMenu.tsx index 3399ed60..b0924f3f 100644 --- a/web-app/src/containers/Tutorial/components/ContentMenu.tsx +++ b/web-app/src/containers/Tutorial/components/ContentMenu.tsx @@ -5,16 +5,16 @@ import { Menu } from '@alifd/next' import Icon from '../../../components/Icon' interface Props { - tutorial: TT.Tutorial + levels: TT.Level[] position: T.Position progress: T.Progress setTitle: (title: string) => void setContent: (content: string) => void } -const ContentMenu = ({ tutorial, position, progress, setTitle, setContent }: Props) => { +const ContentMenu = ({ levels, position, progress, setTitle, setContent }: Props) => { const setMenuContent = (levelId: string) => { - const selectedLevel: TT.Level | undefined = tutorial.levels.find((l: TT.Level) => l.id === levelId) + const selectedLevel: TT.Level | undefined = levels.find((l: TT.Level) => l.id === levelId) if (selectedLevel) { setTitle(selectedLevel.title) setContent(selectedLevel.content) @@ -22,7 +22,7 @@ const ContentMenu = ({ tutorial, position, progress, setTitle, setContent }: Pro } return ( - {tutorial.levels.map((level: TT.Level) => { + {levels.map((level: TT.Level) => { const isCurrent = level.id === position.levelId const isComplete = progress.levels[level.id] let icon diff --git a/web-app/src/containers/Tutorial/components/Level.tsx b/web-app/src/containers/Tutorial/components/Level.tsx index bc738d6d..4a254f47 100644 --- a/web-app/src/containers/Tutorial/components/Level.tsx +++ b/web-app/src/containers/Tutorial/components/Level.tsx @@ -88,7 +88,7 @@ const styles = { } interface Props { - tutorial: TT.Tutorial + tutorial: Exclude index: number status: 'COMPLETE' | 'ACTIVE' | 'INCOMPLETE' progress: T.Progress @@ -114,7 +114,9 @@ const Level = ({ processes, testStatus, }: Props) => { - const level = tutorial.levels[index] + const level: TT.Level = tutorial.levels[index] + + console.log(level) const [title, setTitle] = React.useState(level.title) const [content, setContent] = React.useState(level.content) @@ -135,7 +137,7 @@ const Level = ({ const menu = ( } -const menu = ( - - {[{ id: '1', title: 'First' }].map((level: TT.Level) => { - const icon = - return ( - - {icon}   {level.title} - - ) - })} - -) - -storiesOf('Level', module) - .addDecorator(SideBarDecorator) - .addDecorator(withKnobs) - .add('Level', () => { - const level = { - id: 'L1', - index: 0, +const tutorial: Partial = { + levels: [ + { + id: '1', title: 'A Title', - description: 'A summary of the level', - content: 'Some content here in markdown', - setup: null, - status: 'ACTIVE' as 'ACTIVE', - steps: [ - { - id: 'L1:S1', - title: 'First Step', - content: 'First step description', - setup: { - id: 'L1:S1:SETUP', - commits: ['abcdefg'], - }, - solution: { - id: 'L1:S1:SOLUTION', - commits: ['hijklmn'], - }, - status: 'COMPLETE', - }, - { - id: 'L1:S2', - title: 'Second Step', - content: 'Second step description', - setup: { - id: 'L1:S2:SETUP', - commits: ['abcdefg'], - }, - solution: { - id: 'L1:S2:SOLUTION', - commits: ['hijklmn'], - }, - status: 'ACTIVE', - }, - { - id: 'L1:S3', - title: 'Third Step', - content: 'Third step description', - setup: { - id: 'L1:S3:SETUP', - commits: ['abcdefg'], - }, - solution: { - id: 'L1:S3:SOLUTION', - commits: ['hijklmn'], - }, - status: 'INCOMPLETE', - }, - ], - } - return ( - - ) - }) - .add('Subtasks', () => { - const level = { - id: 'L1', - index: 0, - title: 'A Title', - description: 'A summary of the level', - content: 'Some content here in markdown', + summary: 'A summary of the level', + content: 'Should support markdown test\n ```js\nvar a = 1\n```\nwhew it works!', setup: null, status: 'ACTIVE' as 'ACTIVE', steps: [ { - id: 'L1:S1', - title: 'First Step', - content: 'First step description', + id: '1.1', + content: 'Should support markdown test\n ```shell\nnpn install some-packagen```\nwhew it works!', setup: { - id: 'L1:S1:SETUP', commits: ['abcdefg'], }, solution: { - id: 'L1:S1:SOLUTION', commits: ['hijklmn'], }, status: 'COMPLETE', }, { - id: 'L1:S2', - title: 'Second Step', - content: 'Second step description', + id: '1.2', + content: 'Should support markdown test\n ```js\nvar a = 1\n```\nwhew it works!', setup: { - id: 'L1:S2:SETUP', commits: ['abcdefg'], - filter: '^SomeTest', }, solution: { - id: 'L1:S2:SOLUTION', commits: ['hijklmn'], }, status: 'ACTIVE', }, { - id: 'L1:S3', - title: 'Third Step', - content: 'Third step description', + id: '1.3', + content: 'Should support markdown test\n ```js\nvar a = 1\n```\nwhew it works!', setup: { - id: 'L1:S3:SETUP', commits: ['abcdefg'], }, solution: { - id: 'L1:S3:SOLUTION', commits: ['hijklmn'], }, status: 'INCOMPLETE', }, ], - } - const testStatus: T.TestStatus = { - type: 'error', - title: 'Test Failed because X', - summary: { - 'The first task in a set of multiple subtasks': false, - 'The second task out of a bunch of subtasks': true, - 'The third and final task that has more text and might even wrap around because the text just keeps rambling on longer than anyone would conceivably want to read': false, - }, - } - return ( - - ) - }) - .add('Level 2', () => { - const level = { - id: 'L1', - index: 1, - title: 'A Title', - description: 'A description', - content: 'Should support markdown test\n ```js\nvar a = 1\n```\nwhew it works!', - setup: { commits: ['77e57cd'], commands: ['npm install'], files: [] }, - status: 'ACTIVE' as 'ACTIVE', - steps: [ - { - id: 'L1:S1', - content: 'Should support markdown test\n ```shell\nnpn install some-packagen```\nwhew it works!', - setup: { commits: ['a4679b1'], commands: [], files: ['package.json'] }, - solution: { - commits: ['7c64508'], - commands: ['npm install'], - files: ['package.json'], - }, - status: 'COMPLETE', - }, - { - id: 'L1:S2', - content: 'Should support markdown test\n ```ts\nvar a = 1\n```\nwhew it works!', - setup: { commits: ['8a8a5cb'], commands: [], files: ['src/main.ts'] }, - solution: { commits: ['c2f7973'], commands: [], files: ['src/main.ts'] }, - status: 'COMPLETE', - }, - { - id: 'L1:S3', - content: 'Should support markdown test\n ```js\nvar a = 1\n```\nwhew it works!', - setup: { commits: ['992bcb1'], commands: [], files: ['src/main.ts'] }, - solution: { commits: ['1b92779'], commands: [], files: ['src/main.ts'] }, - status: 'COMPLETE', - }, - { - id: 'L1:S4', - content: 'Should support markdown test\n ```js\nvar a = 1\n```\nwhew it works!', - setup: { commits: ['be32adb'], commands: [], files: ['src/main.ts'] }, - solution: { commits: ['7fe26cb'], commands: [], files: ['src/main.ts'] }, - status: 'COMPLETE', - }, - ], - } - return ( - - ) - }) - .add('FakeBook API L1', () => { - const level = { - id: 'L1', - title: 'Server Setup', - description: 'Configure a GraphQL server using Apollo Server.', - content: - 'Apollo Server is a popular and easy to configure GraphQL server.\n[Apollo Server](https://www.apollographql.com/docs/apollo-server/ee7fbac9c0ca5b1dd6aef886bb695e63/index-diagram.svg)\nBy the end of this lesson you should have your own working server started.', - setup: { - commits: ['6adeb95'], - commands: ['npm install'], - }, - status: 'ACTIVE' as 'ACTIVE', - steps: [ - { - id: 'L1:S1', - content: - 'Start by installing the apollo server dependencies. In a terminal, run:\n ```shell\nnpm install --save apollo-server graphql\n```', - setup: { - files: ['package.json'], - commits: ['b904b87'], - watchers: ['./node_modules/{apollo-server,graphql}/package.json'], - }, - solution: { - files: ['package.json'], - commits: ['ad87a86'], - commands: ['npm install'], - }, - }, - { - id: 'L1:S2', - content: - 'Setup your Apollo Server in `src/main.ts`. Notice that the GraphQL requires two elements: `typeDefs` & `resolvers` - more on these later.\nConfigure the server in the following way:\n```ts\nexport const server = new ApolloServer({\n typeDefs,\n resolvers,\n})\n```', - setup: { - files: ['src/main.ts'], - commits: ['13d8c60'], - }, - solution: { - files: ['src/main.ts'], - commits: ['3dd3500'], - }, - }, - { - id: 'L1:S3', - content: - 'Internally, Apollo is really running an [Express](https://expressjs.com/) server.\nTo start the server call `listen`.\n ```shell\nserver.listen(4000)\n```', - setup: { - files: ['src/main.ts'], - commits: ['1d55cc5'], - }, - solution: { - files: ['src/main.ts'], - commits: ['c92311f'], - }, - }, - { - id: 'L1:S4', - content: - 'GraphQL playground is a UI for viewing your schema & docs and testing queries.\nThe playground can be easily configured, just specify `playground: true` in the config.\n```js\nApolloServer({\n /*...*/\n playground: true,\n})\n```\nVisit http://localhost:4000/graphql to see the playground in action.', - setup: { - files: ['src/main.ts'], - commits: ['50da94e'], - }, - solution: { - files: ['src/main.ts'], - commits: ['865b805'], - }, - }, - ], - } - return ( - - ) - }) - .add('FakeBook API L2', () => { - const level = { - id: 'L2', - title: 'TypeDefs & Resolvers', - description: 'Build endpoints for a User', - content: - 'TypeDefs & Resolvers are the building blocks of GraphQL.\nTypeDefs are the "what", they spell out the shape of the graph. Think of typeDefs like a blueprint.\nResolvers define the "how" and "where", as in how the data is put into the graph and where it comes from.\nNote that there are a few special typeDefs you should know about, but we can start with one: `Query`. `Query` is the entry point for requests.\n', - setup: { - commits: ['0d7543c'], - commands: ['npm install'], - }, - status: 'ACTIVE' as 'ACTIVE', - steps: [ - { - id: 'L2:S1', - content: - 'Start by defining **typeDefs** for the current user, we can call the user `Viewer`.\n```\ntype Query {\n viewer: User\n}\n```\nBut now you will have to define what `User` is below.\n```\ntype User {\n id: ID!\n firstName: String\n lastName: String\n}\n```', - setup: { - files: ['src/typeDefs.ts'], - commits: ['76890db'], - }, - solution: { - commits: ['c5ee208'], - }, - }, - { - id: 'L2:S2', - content: - 'Now that we have typeDefs for `Viewer` and `User` we can define the **resolvers**.\nNotice there is user data located in the `src/data/users.ts` file. Import it.\n```ts\nimport users from "./data/users"\n```\nJust to keep things simple, you can resolve the `viewer` as the user with an id of `1`.\n```ts\nviewer() {\n return users.find(user => user.id === "1")\n}\n```\nRun the server and try your solution in the Playground to see if it worked. The query should look something like\n```\nquery {\n viewer\n}\n```\nHow can it work without resolving the User?', - setup: { - files: ['src/resolvers.ts'], - commits: ['646f930'], - }, - solution: { - commits: ['f382098'], - }, - }, - { - id: 'L2:S3', - content: - 'In GraphQL we follow a process: typeDefs then resolvers. We can practice the pattern\nAdd a list of friends to the user typeDef.\n```\ntype User {\n id: ID!\n firstName: String\n lastName: String\n friends: [User]\n }\n```\nNote that a typesDef can even call itself!', - setup: { - files: ['src/typeDefs.ts'], - commits: ['f00e6e6'], - }, - solution: { - commits: ['04fb044'], - }, - }, - { - id: 'L2:S3', - content: - 'Next we can load the friends with resolvers. If you look in `src/data/users` you can see that users are stored as user ids.\n```json\n{\n id: 1,\n jsonfriends: [ "19", "22", "30" ]\n}\n```\nWe need to tell graphql to resolve the user ids on friends. We can use the first param passed into resolvers - often called **parent**. The parent `Viewer` passes params down to the child `User` which uses the ids to resolve the next users.\n```ts\nUser: {\n friends(parent) {\n const userFriends = parent.friends\n return users.filter(user => userFriends.includes(user.id))\n }\n}\n```', - setup: { - files: ['src/resolvers.ts'], - commits: ['932a621'], - }, - solution: { - commits: ['bd79f75'], - }, - }, - ], - } - return ( - - ) - }) - .add('No steps', () => { - const level = { - id: 'L1', - index: 0, - title: 'A Title', - description: 'A summary of the level', - content: 'Some content here in markdown', - setup: null, - status: 'ACTIVE' as 'ACTIVE', - steps: [], - } - return ( - - ) - }) + }, + ], +} + +storiesOf('Level', module) + .addDecorator(SideBarDecorator) + .addDecorator(withKnobs) + .add('Level', () => ( + + )) + .add('Level 2', () => ( + + )) + .add('No steps', () => ( + + )) + .add('No lesson', () => ( + + ))