From 12962fa611f1d0e4b5b69b89d6a40b805c533a1f Mon Sep 17 00:00:00 2001 From: shmck Date: Sun, 17 Nov 2019 11:49:14 -0800 Subject: [PATCH 01/15] update linter --- web-app/package-lock.json | 6 +++--- web-app/package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/web-app/package-lock.json b/web-app/package-lock.json index 1535cbc5..35871dad 100644 --- a/web-app/package-lock.json +++ b/web-app/package-lock.json @@ -6615,9 +6615,9 @@ } }, "eslint-config-prettier": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.5.0.tgz", - "integrity": "sha512-cjXp8SbO9VFGW/Z7mbTydqS9to8Z58E5aYhj3e1+Hx7lS9s6gL5ILKNpCqZAFOVYRcSkWPFYljHrEh8QFEK5EQ==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.6.0.tgz", + "integrity": "sha512-6RGaj7jD+HeuSVHoIT6A0WkBhVEk0ULg74kp2FAWIwkYrOERae0TjIO09Cw33oN//gJWmt7aFhVJErEVta7uvA==", "dev": true, "requires": { "get-stdin": "^6.0.0" diff --git a/web-app/package.json b/web-app/package.json index a2fe8e39..029d2de8 100644 --- a/web-app/package.json +++ b/web-app/package.json @@ -65,7 +65,7 @@ "babel-loader": "8.0.5", "babel-plugin-import": "^1.12.1", "eslint": "^6.6.0", - "eslint-config-prettier": "^6.5.0", + "eslint-config-prettier": "^6.6.0", "eslint-plugin-prettier": "^3.1.1", "node-sass": "^4.13.0", "prettier": "^1.19.1", From 121d5cbca387ebc561577d413510a32c2f7d515c Mon Sep 17 00:00:00 2001 From: shmck Date: Sun, 17 Nov 2019 11:49:57 -0800 Subject: [PATCH 02/15] create indeterminate loading checkbox --- web-app/src/components/Checkbox/index.tsx | 66 +++++++++++++++-------- 1 file changed, 44 insertions(+), 22 deletions(-) diff --git a/web-app/src/components/Checkbox/index.tsx b/web-app/src/components/Checkbox/index.tsx index f22b04b1..2b60d046 100644 --- a/web-app/src/components/Checkbox/index.tsx +++ b/web-app/src/components/Checkbox/index.tsx @@ -1,34 +1,56 @@ import * as React from 'react' const styles = { - box: { - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - }, - input: { - border: '1px solid black', - backgroundColor: 'yellow', - }, + box: { + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + }, + input: { + border: '1px solid black', + }, + loading: { + backgroundColor: 'red', + }, } interface Props { - status: 'COMPLETE' | 'INCOMPLETE' | 'ACTIVE' | 'LOADING' + status: 'COMPLETE' | 'INCOMPLETE' | 'ACTIVE' | 'LOADING' } const Checkbox = (props: Props) => { - const checked = props.status === 'COMPLETE' - // const loading = props.state === 'LOADING' - const onChange = () => { - /* read */ - } - return ( -
- -
- ) + const onChange = () => { + /* read only */ + } + + if (props.status === 'LOADING') { + return ( +
+ { + /* ref because unable to apply indeterminate on jsx */ + if (input) { + input.indeterminate = true + } + }} + type="checkbox" + checked={false} + disabled={true} + onChange={onChange} + /> +
+ ) + } + + const checked = props.status === 'COMPLETE' + + return ( +
+ +
+ ) } export default Checkbox From 3888e8b0200cb032d66016bfa38ca10909597b8e Mon Sep 17 00:00:00 2001 From: shmck Date: Sun, 17 Nov 2019 11:55:10 -0800 Subject: [PATCH 03/15] add relative positioning to pages for storybook --- web-app/src/components/Checkbox/index.tsx | 21 +-- web-app/src/containers/Continue/index.tsx | 63 +++---- web-app/src/containers/LoadingPage.tsx | 21 ++- web-app/src/containers/New/NewPage.tsx | 51 ++--- .../src/containers/Overview/OverviewPage.tsx | 139 +++++++------- .../Tutorial/LevelPage/Level/index.tsx | 175 +++++++++--------- 6 files changed, 228 insertions(+), 242 deletions(-) diff --git a/web-app/src/components/Checkbox/index.tsx b/web-app/src/components/Checkbox/index.tsx index 2b60d046..9508aac6 100644 --- a/web-app/src/components/Checkbox/index.tsx +++ b/web-app/src/components/Checkbox/index.tsx @@ -15,7 +15,7 @@ const styles = { } interface Props { - status: 'COMPLETE' | 'INCOMPLETE' | 'ACTIVE' | 'LOADING' + status: 'COMPLETE' | 'INCOMPLETE' | 'ACTIVE' } const Checkbox = (props: Props) => { @@ -23,25 +23,6 @@ const Checkbox = (props: Props) => { /* read only */ } - if (props.status === 'LOADING') { - return ( -
- { - /* ref because unable to apply indeterminate on jsx */ - if (input) { - input.indeterminate = true - } - }} - type="checkbox" - checked={false} - disabled={true} - onChange={onChange} - /> -
- ) - } - const checked = props.status === 'COMPLETE' return ( diff --git a/web-app/src/containers/Continue/index.tsx b/web-app/src/containers/Continue/index.tsx index 7b4fc4a8..9f0a5dbc 100644 --- a/web-app/src/containers/Continue/index.tsx +++ b/web-app/src/containers/Continue/index.tsx @@ -5,51 +5,52 @@ import * as CR from 'typings' import * as G from 'typings/graphql' const styles = { - page: { - width: '100%', - }, + page: { + position: 'relative' as 'relative', + width: '100%', + }, } interface Props { - tutorial: G.Tutorial - onContinue(): void - onNew(): void + tutorial: G.Tutorial + onContinue(): void + onNew(): void } export const ContinuePage = (props: Props) => ( -
-

Continue

- -
-

{props.tutorial.version.summary.title}

-

{props.tutorial.version.summary.description}

- -
-
- -
-

Start a New Tutorial

- -
-
-
+
+

Continue

+ +
+

{props.tutorial.version.summary.title}

+

{props.tutorial.version.summary.description}

+ +
+
+ +
+

Start a New Tutorial

+ +
+
+
) interface ContainerProps { - context: CR.MachineContext - send(action: CR.Action | string): void + context: CR.MachineContext + send(action: CR.Action | string): void } const ContinuePageContainer = ({ context, send }: ContainerProps) => { - const { tutorial } = context + const { tutorial } = context - if (!tutorial) { - throw new Error('Tutorial not found') - } + if (!tutorial) { + throw new Error('Tutorial not found') + } - return ( - send('TUTORIAL_START')} onNew={() => send('TUTORIAL_SELECT')} /> - ) + return ( + send('TUTORIAL_START')} onNew={() => send('TUTORIAL_SELECT')} /> + ) } export default ContinuePageContainer diff --git a/web-app/src/containers/LoadingPage.tsx b/web-app/src/containers/LoadingPage.tsx index a9034be8..8fd5ca24 100644 --- a/web-app/src/containers/LoadingPage.tsx +++ b/web-app/src/containers/LoadingPage.tsx @@ -2,22 +2,23 @@ import * as React from 'react' import Loading from '../components/Loading' interface Props { - text: string + text: string } const styles = { - page: { - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - width: '100%', - }, + page: { + position: 'relative' as 'relative', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + width: '100%', + }, } const LoadingPage = ({ text }: Props) => ( -
- -
+
+ +
) export default LoadingPage diff --git a/web-app/src/containers/New/NewPage.tsx b/web-app/src/containers/New/NewPage.tsx index 0aeb52ef..b3a96f3c 100644 --- a/web-app/src/containers/New/NewPage.tsx +++ b/web-app/src/containers/New/NewPage.tsx @@ -3,37 +3,38 @@ import * as G from 'typings/graphql' import TutorialList from './TutorialList' const styles = { - page: { - width: '100%', - }, - header: { - height: '36px', - backgroundColor: '#EBEBEB', - fontSize: '16px', - lineHeight: '16px', - padding: '10px 1rem', - }, - banner: { - height: '50px', - fontSize: '1rem', - padding: '1rem', - }, + page: { + position: 'relative' as 'relative', + width: '100%', + }, + header: { + height: '36px', + backgroundColor: '#EBEBEB', + fontSize: '16px', + lineHeight: '16px', + padding: '10px 1rem', + }, + banner: { + height: '50px', + fontSize: '1rem', + padding: '1rem', + }, } interface Props { - tutorialList: G.Tutorial[] + tutorialList: G.Tutorial[] } const NewPage = (props: Props) => ( -
-
- CodeRoad -
-
- Select a Tutorial to Start -
- -
+
+
+ CodeRoad +
+
+ Select a Tutorial to Start +
+ +
) export default NewPage diff --git a/web-app/src/containers/Overview/OverviewPage.tsx b/web-app/src/containers/Overview/OverviewPage.tsx index 7da0aacd..ecd64469 100644 --- a/web-app/src/containers/Overview/OverviewPage.tsx +++ b/web-app/src/containers/Overview/OverviewPage.tsx @@ -5,82 +5,83 @@ import * as G from 'typings/graphql' import Markdown from '../../components/Markdown' const styles = { - page: { - width: '100%', - }, - summary: { - padding: '0rem 1rem 1rem 1rem', - }, - title: { - fontWeight: 'bold' as 'bold', - }, - description: { - fontSize: '1rem', - }, - header: { - height: '36px', - backgroundColor: '#EBEBEB', - fontSize: '16px', - lineHeight: '16px', - padding: '10px 1rem', - }, - levelList: { - padding: '0rem 1rem', - }, - options: { - display: 'flex' as 'flex', - flexDirection: 'row' as 'row', - alignItems: 'center' as 'center', - justifyContent: 'flex-end' as 'flex-end', - position: 'absolute' as 'absolute', - bottom: 0, - height: '50px', - padding: '1rem', - paddingRight: '2rem', - backgroundColor: 'black', - width: '100%', - }, + page: { + position: 'relative' as 'relative', + width: '100%', + }, + summary: { + padding: '0rem 1rem 1rem 1rem', + }, + title: { + fontWeight: 'bold' as 'bold', + }, + description: { + fontSize: '1rem', + }, + header: { + height: '36px', + backgroundColor: '#EBEBEB', + fontSize: '16px', + lineHeight: '16px', + padding: '10px 1rem', + }, + levelList: { + padding: '0rem 1rem', + }, + options: { + display: 'flex' as 'flex', + flexDirection: 'row' as 'row', + alignItems: 'center' as 'center', + justifyContent: 'flex-end' as 'flex-end', + position: 'absolute' as 'absolute', + bottom: 0, + height: '50px', + padding: '1rem', + paddingRight: '2rem', + backgroundColor: 'black', + width: '100%', + }, } interface Props { - title: string - description: string - levels: G.Level[] - onNext(): void + title: string + description: string + levels: G.Level[] + onNext(): void } const Summary = ({ title, description, levels, onNext }: Props) => ( -
-
- CodeRoad -
-
-

{title}

- {description} -
-
-
- Levels -
-
- {levels.map((level: G.Level, index: number) => ( -
-

- {index + 1}. {level.title} -

-
{level.description}
-
- ))} -
-
+
+
+ CodeRoad +
+
+

{title}

+ {description} +
+
+
+ Levels +
+
+ {levels.map((level: G.Level, index: number) => ( +
+

+ {index + 1}. {level.title} +

+
{level.description}
+
+ ))} +
+
-
- {/* TODO: Add back button */} - -
-
+
+ {/* TODO: Add back button */} + +
+
) export default Summary diff --git a/web-app/src/containers/Tutorial/LevelPage/Level/index.tsx b/web-app/src/containers/Tutorial/LevelPage/Level/index.tsx index dfdc58f2..6e69b171 100644 --- a/web-app/src/containers/Tutorial/LevelPage/Level/index.tsx +++ b/web-app/src/containers/Tutorial/LevelPage/Level/index.tsx @@ -7,102 +7,103 @@ import Button from '../../../../components/Button' import Markdown from '../../../../components/Markdown' const styles = { - card: { - padding: 0, - width: '100%', - }, - header: { - height: '36px', - backgroundColor: '#EBEBEB', - fontSize: '16px', - lineHeight: '16px', - padding: '10px 1rem', - }, - content: { - padding: '0rem 1rem', - paddingBottom: '1rem', - }, - options: { - padding: '0rem 1rem', - }, - steps: { - padding: '1rem 16px', - }, - title: { - fontSize: '1.2rem', - fontWeight: 'bold' as 'bold', - lineHeight: '1.2rem', - }, - footer: { - height: '36px', - backgroundColor: 'black', - fontSize: '16px', - lineHeight: '16px', - padding: '10px 1rem', - color: 'white', - position: 'absolute' as 'absolute', - bottom: 0, - width: '100%', - }, + card: { + position: 'relative' as 'relative', + padding: 0, + width: '100%', + }, + header: { + height: '36px', + backgroundColor: '#EBEBEB', + fontSize: '16px', + lineHeight: '16px', + padding: '10px 1rem', + }, + content: { + padding: '0rem 1rem', + paddingBottom: '1rem', + }, + options: { + padding: '0rem 1rem', + }, + steps: { + padding: '1rem 16px', + }, + title: { + fontSize: '1.2rem', + fontWeight: 'bold' as 'bold', + lineHeight: '1.2rem', + }, + footer: { + height: '36px', + backgroundColor: 'black', + fontSize: '16px', + lineHeight: '16px', + padding: '10px 1rem', + color: 'white', + position: 'absolute' as 'absolute', + bottom: 0, + width: '100%', + }, } interface Props { - level: G.Level & { status: T.ProgressStatus; index: number; steps: Array } - onContinue(): void - onLoadSolution(): void + level: G.Level & { status: T.ProgressStatus; index: number; steps: Array } + onContinue(): void + onLoadSolution(): void } const Level = ({ level, onContinue, onLoadSolution }: Props) => { - if (!level.steps) { - throw new Error('No Stage steps found') - } + if (!level.steps) { + throw new Error('No Stage steps found') + } - return ( -
-
-
- Learn -
-
-

{level.title}

- {level.content || ''} -
-
+ return ( +
+
+
+ Learn +
+
+

{level.title}

+ {level.content || ''} +
+
-
-
Tasks
-
- {level.steps.map((step: (G.Step & { status: T.ProgressStatus }) | null, index: number) => { - if (!step) { - return null - } - return ( - - ) - })} -
-
+
+
Tasks
+
+ {level.steps.map((step: (G.Step & { status: T.ProgressStatus }) | null, index: number) => { + if (!step) { + return null + } + return ( + + ) + })} +
+
- {level.status === 'COMPLETE' && ( -
- -
- )} -
-
- - {level.index ? `${level.index.toString()}.` : ''} {level.title} - -
-
-
- ) + {level.status === 'COMPLETE' && ( +
+ +
+ )} +
+
+ + {level.index ? `${level.index.toString()}.` : ''} {level.title} + +
+
+
+ ) } export default Level From d0b8ee670c07475586efbad4a2d56c4b0269060d Mon Sep 17 00:00:00 2001 From: shmck Date: Sun, 17 Nov 2019 12:09:43 -0800 Subject: [PATCH 04/15] setup processes component --- web-app/src/components/Processes/index.tsx | 30 +++++++++++++++++++ web-app/stories/Checkbox.stories.tsx | 35 ++++++++++------------ web-app/stories/Commands.stories.tsx | 29 ++++++++++++++++++ 3 files changed, 75 insertions(+), 19 deletions(-) create mode 100644 web-app/src/components/Processes/index.tsx create mode 100644 web-app/stories/Commands.stories.tsx diff --git a/web-app/src/components/Processes/index.tsx b/web-app/src/components/Processes/index.tsx new file mode 100644 index 00000000..f9224b60 --- /dev/null +++ b/web-app/src/components/Processes/index.tsx @@ -0,0 +1,30 @@ +import * as React from 'react' +import { Message as AlifdMessage } from '@alifd/next' + +interface Process { + title: string + description: string +} + +interface Props { + processes: Process[] +} + +const styles = { + container: { + display: 'flex', + flexDirection: 'column' as 'column', + }, +} + +const Processes = (props: Props) => ( +
+ {props.processes.map(process => ( + + {process.description}... + + ))} +
+) + +export default Processes diff --git a/web-app/stories/Checkbox.stories.tsx b/web-app/stories/Checkbox.stories.tsx index d0ef0e7f..0fa392d6 100644 --- a/web-app/stories/Checkbox.stories.tsx +++ b/web-app/stories/Checkbox.stories.tsx @@ -5,24 +5,21 @@ import SideBarDecorator from './utils/SideBarDecorator' import Checkbox from '../src/components/Checkbox' const styles = { - container: { - display: 'flex' as 'flex', - flexDirection: 'column' as 'column', - }, + container: { + display: 'flex' as 'flex', + flexDirection: 'column' as 'column', + }, } -storiesOf('Checkbox', module) - .addDecorator(SideBarDecorator) - .add('Checkboxes', () => ( -
- - Checked - - - Unchecked - - - Loading - -
- )) +storiesOf('Components', module) + .addDecorator(SideBarDecorator) + .add('Checkboxes', () => ( +
+ + Checked + + + Unchecked + +
+ )) diff --git a/web-app/stories/Commands.stories.tsx b/web-app/stories/Commands.stories.tsx new file mode 100644 index 00000000..99755308 --- /dev/null +++ b/web-app/stories/Commands.stories.tsx @@ -0,0 +1,29 @@ +import React from 'react' +import { storiesOf } from '@storybook/react' +import SideBarDecorator from './utils/SideBarDecorator' + +import Processes from '../src/components/Processes' + +const styles = { + container: { + display: 'flex' as 'flex', + flexDirection: 'column' as 'column', + }, +} + +storiesOf('Components', module) + .addDecorator(SideBarDecorator) + .add('Processes', () => ( + + )) From 70119ed5c901004ff096e9aa4195c3347dcea610 Mon Sep 17 00:00:00 2001 From: shmck Date: Sun, 17 Nov 2019 14:04:44 -0800 Subject: [PATCH 05/15] refactor level & step file structure --- .../LevelPage/{Level/index.tsx => Level.tsx} | 4 +- .../Tutorial/LevelPage/Level/Step/index.tsx | 57 ----- .../containers/Tutorial/LevelPage/Step.tsx | 57 +++++ web-app/stories/Level.stories.tsx | 216 +++++++++--------- web-app/stories/Step.stories.tsx | 40 ++-- 5 files changed, 187 insertions(+), 187 deletions(-) rename web-app/src/containers/Tutorial/LevelPage/{Level/index.tsx => Level.tsx} (95%) delete mode 100644 web-app/src/containers/Tutorial/LevelPage/Level/Step/index.tsx create mode 100644 web-app/src/containers/Tutorial/LevelPage/Step.tsx diff --git a/web-app/src/containers/Tutorial/LevelPage/Level/index.tsx b/web-app/src/containers/Tutorial/LevelPage/Level.tsx similarity index 95% rename from web-app/src/containers/Tutorial/LevelPage/Level/index.tsx rename to web-app/src/containers/Tutorial/LevelPage/Level.tsx index 6e69b171..47bd5060 100644 --- a/web-app/src/containers/Tutorial/LevelPage/Level/index.tsx +++ b/web-app/src/containers/Tutorial/LevelPage/Level.tsx @@ -3,8 +3,8 @@ import * as G from 'typings/graphql' import * as T from 'typings' import Step from './Step' -import Button from '../../../../components/Button' -import Markdown from '../../../../components/Markdown' +import Button from '../../../components/Button' +import Markdown from '../../../components/Markdown' const styles = { card: { diff --git a/web-app/src/containers/Tutorial/LevelPage/Level/Step/index.tsx b/web-app/src/containers/Tutorial/LevelPage/Level/Step/index.tsx deleted file mode 100644 index 0d75eba6..00000000 --- a/web-app/src/containers/Tutorial/LevelPage/Level/Step/index.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import * as React from 'react' -import * as T from 'typings' -import Checkbox from '../../../../../components/Checkbox' -import Markdown from '../../../../../components/Markdown' -import StepHelp from '../../../../../components/StepHelp' - -interface Props { - order: number - content: string - status: T.ProgressStatus - onLoadSolution(): void -} - -const styles = { - card: { - display: 'grid', - gridTemplateColumns: '25px 1fr', - padding: '1rem 1rem 1rem 0.2rem', - }, - content: { - margin: 0, - }, - options: { - display: 'flex' as 'flex', - flexDirection: 'row' as 'row', - justifyContent: 'flex-end', - alignItems: 'center' as 'center', - padding: '0.5rem', - }, -} - -const Step = (props: Props) => { - const showStep = props.status !== 'INCOMPLETE' - if (!showStep) { - return null - } - const showLoadSolution = props.status === 'ACTIVE' - return ( -
-
-
- -
-
- {props.content || ''} -
-
- {showLoadSolution && ( -
- -
- )} -
- ) -} - -export default Step diff --git a/web-app/src/containers/Tutorial/LevelPage/Step.tsx b/web-app/src/containers/Tutorial/LevelPage/Step.tsx new file mode 100644 index 00000000..515bf586 --- /dev/null +++ b/web-app/src/containers/Tutorial/LevelPage/Step.tsx @@ -0,0 +1,57 @@ +import * as React from 'react' +import * as T from 'typings' +import Checkbox from '../../../components/Checkbox' +import Markdown from '../../../components/Markdown' +import StepHelp from '../../../components/StepHelp' + +interface Props { + order: number + content: string + status: T.ProgressStatus + onLoadSolution(): void +} + +const styles = { + card: { + display: 'grid', + gridTemplateColumns: '25px 1fr', + padding: '1rem 1rem 1rem 0.2rem', + }, + content: { + margin: 0, + }, + options: { + display: 'flex' as 'flex', + flexDirection: 'row' as 'row', + justifyContent: 'flex-end', + alignItems: 'center' as 'center', + padding: '0.5rem', + }, +} + +const Step = (props: Props) => { + const showStep = props.status !== 'INCOMPLETE' + if (!showStep) { + return null + } + const showLoadSolution = props.status === 'ACTIVE' + return ( +
+
+
+ +
+
+ {props.content || ''} +
+
+ {showLoadSolution && ( +
+ +
+ )} +
+ ) +} + +export default Step diff --git a/web-app/stories/Level.stories.tsx b/web-app/stories/Level.stories.tsx index 4b0d81f0..27999ad2 100644 --- a/web-app/stories/Level.stories.tsx +++ b/web-app/stories/Level.stories.tsx @@ -7,116 +7,116 @@ import { withKnobs } from '@storybook/addon-knobs' import { storiesOf } from '@storybook/react' import SideBarDecorator from './utils/SideBarDecorator' -import Level from '../src/containers/Tutorial/LevelPage/Level/index' +import Level from '../src/containers/Tutorial/LevelPage/Level' type ModifiedLevel = G.Level & { - status: T.ProgressStatus - index: number - steps: Array + status: T.ProgressStatus + index: number + steps: Array } storiesOf('Level', module) - .addDecorator(SideBarDecorator) - .addDecorator(withKnobs) - .add('Level', () => { - const level = { - id: 'L1', - index: 2, - title: 'A Title', - description: 'A summary of the level', - content: 'Some content here in markdown', - setup: null, - status: '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('Level 2', () => { - const level = { - id: 'L1', - 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: [] }, - 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: 'ACTIVE', - }, - { - 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: 'INCOMPLETE', - }, - { - 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: 'INCOMPLETE', - }, - { - 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: 'INCOMPLETE', - }, - ], - index: 0, - status: 'ACTIVE', - } - return - }) + .addDecorator(SideBarDecorator) + .addDecorator(withKnobs) + .add('Level', () => { + const level = { + id: 'L1', + index: 2, + title: 'A Title', + description: 'A summary of the level', + content: 'Some content here in markdown', + setup: null, + status: '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('Level 2', () => { + const level = { + id: 'L1', + 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: [] }, + 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: 'ACTIVE', + }, + { + 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: 'INCOMPLETE', + }, + { + 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: 'INCOMPLETE', + }, + { + 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: 'INCOMPLETE', + }, + ], + index: 0, + status: 'ACTIVE', + } + return + }) diff --git a/web-app/stories/Step.stories.tsx b/web-app/stories/Step.stories.tsx index 3bf5b8ec..c8cc5f35 100644 --- a/web-app/stories/Step.stories.tsx +++ b/web-app/stories/Step.stories.tsx @@ -5,10 +5,10 @@ import { select, text, withKnobs } from '@storybook/addon-knobs' import { storiesOf } from '@storybook/react' import SideBarDecorator from './utils/SideBarDecorator' -import Step from '../src/containers/Tutorial/LevelPage/Level/Step' +import Step from '../src/containers/Tutorial/LevelPage/Step' const stepText = - 'This is a long paragraph of step text intended to wrap around the side after a short period of writing to demonstrate text wrap among other things' + 'This is a long paragraph of step text intended to wrap around the side after a short period of writing to demonstrate text wrap among other things' const paragraphText = `Markdown included \`code\`, *bold*, & _italics_. \`\`\`javascript @@ -31,21 +31,21 @@ const paragraphText = `Markdown included \`code\`, *bold*, & _italics_. ` storiesOf('Level', module) - .addDecorator(SideBarDecorator) - .addDecorator(withKnobs) - .add('Step', () => ( - - )) - .add('Step Markdown', () => ( - - )) + .addDecorator(SideBarDecorator) + .addDecorator(withKnobs) + .add('Step', () => ( + + )) + .add('Step Markdown', () => ( + + )) From 0ee8b05ca9b0e33f91d1046a869ab81ee86bf2fb Mon Sep 17 00:00:00 2001 From: shmck Date: Sun, 17 Nov 2019 14:12:03 -0800 Subject: [PATCH 06/15] style without absolute positioning --- .../src/containers/Overview/OverviewPage.tsx | 42 ++++++++++--------- .../containers/Tutorial/LevelPage/Level.tsx | 10 +++-- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/web-app/src/containers/Overview/OverviewPage.tsx b/web-app/src/containers/Overview/OverviewPage.tsx index ecd64469..3298440f 100644 --- a/web-app/src/containers/Overview/OverviewPage.tsx +++ b/web-app/src/containers/Overview/OverviewPage.tsx @@ -7,7 +7,11 @@ import Markdown from '../../components/Markdown' const styles = { page: { position: 'relative' as 'relative', + display: 'flex' as 'flex', + flexDirection: 'column' as 'column', + justifyContent: 'space-between', width: '100%', + height: '100%', }, summary: { padding: '0rem 1rem 1rem 1rem', @@ -33,8 +37,6 @@ const styles = { flexDirection: 'row' as 'row', alignItems: 'center' as 'center', justifyContent: 'flex-end' as 'flex-end', - position: 'absolute' as 'absolute', - bottom: 0, height: '50px', padding: '1rem', paddingRight: '2rem', @@ -52,26 +54,28 @@ interface Props { const Summary = ({ title, description, levels, onNext }: Props) => (
-
- CodeRoad -
-
-

{title}

- {description} -
- Levels + CodeRoad +
+
+

{title}

+ {description}
-
- {levels.map((level: G.Level, index: number) => ( -
-

- {index + 1}. {level.title} -

-
{level.description}
-
- ))} +
+
+ Levels +
+
+ {levels.map((level: G.Level, index: number) => ( +
+

+ {index + 1}. {level.title} +

+
{level.description}
+
+ ))} +
diff --git a/web-app/src/containers/Tutorial/LevelPage/Level.tsx b/web-app/src/containers/Tutorial/LevelPage/Level.tsx index 47bd5060..3569cffc 100644 --- a/web-app/src/containers/Tutorial/LevelPage/Level.tsx +++ b/web-app/src/containers/Tutorial/LevelPage/Level.tsx @@ -7,10 +7,14 @@ import Button from '../../../components/Button' import Markdown from '../../../components/Markdown' const styles = { - card: { + page: { position: 'relative' as 'relative', + display: 'flex' as 'flex', + flexDirection: 'column' as 'column', + justifyContent: 'space-between', padding: 0, width: '100%', + height: '100%', }, header: { height: '36px', @@ -41,8 +45,6 @@ const styles = { lineHeight: '16px', padding: '10px 1rem', color: 'white', - position: 'absolute' as 'absolute', - bottom: 0, width: '100%', }, } @@ -59,7 +61,7 @@ const Level = ({ level, onContinue, onLoadSolution }: Props) => { } return ( -
+
Learn From 5133dadf3088edc1e55ee4f94e12e4aacb25d3f9 Mon Sep 17 00:00:00 2001 From: shmck Date: Sun, 17 Nov 2019 14:19:55 -0800 Subject: [PATCH 07/15] fix level index --- web-app/src/containers/Tutorial/LevelPage/Level.tsx | 3 ++- web-app/stories/Level.stories.tsx | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/web-app/src/containers/Tutorial/LevelPage/Level.tsx b/web-app/src/containers/Tutorial/LevelPage/Level.tsx index 3569cffc..404b2873 100644 --- a/web-app/src/containers/Tutorial/LevelPage/Level.tsx +++ b/web-app/src/containers/Tutorial/LevelPage/Level.tsx @@ -100,7 +100,8 @@ const Level = ({ level, onContinue, onLoadSolution }: Props) => {
- {level.index ? `${level.index.toString()}.` : ''} {level.title} + {typeof level.index === 'number' ? `${level.index + 1}. ` : ''} + {level.title}
diff --git a/web-app/stories/Level.stories.tsx b/web-app/stories/Level.stories.tsx index 27999ad2..0c667b69 100644 --- a/web-app/stories/Level.stories.tsx +++ b/web-app/stories/Level.stories.tsx @@ -21,7 +21,7 @@ storiesOf('Level', module) .add('Level', () => { const level = { id: 'L1', - index: 2, + index: 0, title: 'A Title', description: 'A summary of the level', content: 'Some content here in markdown', @@ -77,6 +77,7 @@ storiesOf('Level', module) .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!', @@ -115,7 +116,6 @@ storiesOf('Level', module) status: 'INCOMPLETE', }, ], - index: 0, status: 'ACTIVE', } return From 35991376ce6c500de689ca691230c2b420d6f227 Mon Sep 17 00:00:00 2001 From: shmck Date: Sun, 17 Nov 2019 14:27:06 -0800 Subject: [PATCH 08/15] setup tutorial level process events viewer --- typings/index.d.ts | 5 +++ .../src/components/ProcessEvents/index.tsx | 37 +++++++++++++++++++ web-app/src/components/Processes/index.tsx | 30 --------------- .../containers/Tutorial/LevelPage/Level.tsx | 29 +++++++++------ web-app/stories/Level.stories.tsx | 18 ++++++++- 5 files changed, 76 insertions(+), 43 deletions(-) create mode 100644 web-app/src/components/ProcessEvents/index.tsx delete mode 100644 web-app/src/components/Processes/index.tsx diff --git a/typings/index.d.ts b/typings/index.d.ts index 26c6f17a..d123b46c 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -103,3 +103,8 @@ interface MessageState { // todo: type each string param and payload export type EditorDispatch = (type: string, payload?: MessageData | MessageState | any) => void + +export interface ProcessEvent { + title: string + description: string +} diff --git a/web-app/src/components/ProcessEvents/index.tsx b/web-app/src/components/ProcessEvents/index.tsx new file mode 100644 index 00000000..01dcb371 --- /dev/null +++ b/web-app/src/components/ProcessEvents/index.tsx @@ -0,0 +1,37 @@ +import * as React from 'react' +import { Message as AlifdMessage } from '@alifd/next' +import * as T from 'typings' + +interface Process { + title: string + description: string +} + +interface Props { + processes: T.ProcessEvent[] +} + +const styles = { + container: { + display: 'flex', + flexDirection: 'column' as 'column', + }, +} + +// display a list of active processes +const ProcessEvents = (props: Props) => { + if (!props.processes.length) { + return null + } + return ( +
+ {props.processes.map(process => ( + + {process.description}... + + ))} +
+ ) +} + +export default ProcessEvents diff --git a/web-app/src/components/Processes/index.tsx b/web-app/src/components/Processes/index.tsx deleted file mode 100644 index f9224b60..00000000 --- a/web-app/src/components/Processes/index.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import * as React from 'react' -import { Message as AlifdMessage } from '@alifd/next' - -interface Process { - title: string - description: string -} - -interface Props { - processes: Process[] -} - -const styles = { - container: { - display: 'flex', - flexDirection: 'column' as 'column', - }, -} - -const Processes = (props: Props) => ( -
- {props.processes.map(process => ( - - {process.description}... - - ))} -
-) - -export default Processes diff --git a/web-app/src/containers/Tutorial/LevelPage/Level.tsx b/web-app/src/containers/Tutorial/LevelPage/Level.tsx index 404b2873..35f97c48 100644 --- a/web-app/src/containers/Tutorial/LevelPage/Level.tsx +++ b/web-app/src/containers/Tutorial/LevelPage/Level.tsx @@ -5,6 +5,7 @@ import * as T from 'typings' import Step from './Step' import Button from '../../../components/Button' import Markdown from '../../../components/Markdown' +import ProcessEvents from '../../../components/ProcessEvents' const styles = { page: { @@ -51,11 +52,12 @@ const styles = { interface Props { level: G.Level & { status: T.ProgressStatus; index: number; steps: Array } + processes: T.ProcessEvent[] onContinue(): void onLoadSolution(): void } -const Level = ({ level, onContinue, onLoadSolution }: Props) => { +const Level = ({ level, onContinue, onLoadSolution, processes }: Props) => { if (!level.steps) { throw new Error('No Stage steps found') } @@ -92,17 +94,22 @@ const Level = ({ level, onContinue, onLoadSolution }: Props) => {
- {level.status === 'COMPLETE' && ( -
- -
- )}
-
- - {typeof level.index === 'number' ? `${level.index + 1}. ` : ''} - {level.title} - + {level.status === 'COMPLETE' && ( +
+ +
+ )} + + + +
+
+ + {typeof level.index === 'number' ? `${level.index + 1}. ` : ''} + {level.title} + +
diff --git a/web-app/stories/Level.stories.tsx b/web-app/stories/Level.stories.tsx index 0c667b69..dca97964 100644 --- a/web-app/stories/Level.stories.tsx +++ b/web-app/stories/Level.stories.tsx @@ -72,7 +72,9 @@ storiesOf('Level', module) }, ], } - return + return ( + + ) }) .add('Level 2', () => { const level = { @@ -118,5 +120,17 @@ storiesOf('Level', module) ], status: 'ACTIVE', } - return + return ( + + ) }) From aa978afefb3b48de352da3aa065d3a7b789a78cd Mon Sep 17 00:00:00 2001 From: shmck Date: Sun, 17 Nov 2019 15:01:06 -0800 Subject: [PATCH 09/15] define processes --- typings/index.d.ts | 2 + .../src/components/ProcessEvents/index.tsx | 3 - .../containers/Tutorial/LevelPage/Level.tsx | 9 +- .../containers/Tutorial/LevelPage/index.tsx | 82 +++++++++---------- web-app/src/services/state/machine.ts | 1 + 5 files changed, 52 insertions(+), 45 deletions(-) diff --git a/typings/index.d.ts b/typings/index.d.ts index d123b46c..3a306068 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -43,6 +43,7 @@ export interface MachineContext { tutorial: G.Tutorial | null position: Position progress: Progress + processes: ProcessEvent[] } export interface MachineEvent { @@ -107,4 +108,5 @@ export type EditorDispatch = (type: string, payload?: MessageData | MessageState export interface ProcessEvent { title: string description: string + status: 'RUNNING' | 'SUCCESS' | 'FAIL' | 'ERROR' } diff --git a/web-app/src/components/ProcessEvents/index.tsx b/web-app/src/components/ProcessEvents/index.tsx index 01dcb371..79c81db2 100644 --- a/web-app/src/components/ProcessEvents/index.tsx +++ b/web-app/src/components/ProcessEvents/index.tsx @@ -20,9 +20,6 @@ const styles = { // display a list of active processes const ProcessEvents = (props: Props) => { - if (!props.processes.length) { - return null - } return (
{props.processes.map(process => ( diff --git a/web-app/src/containers/Tutorial/LevelPage/Level.tsx b/web-app/src/containers/Tutorial/LevelPage/Level.tsx index 35f97c48..3e46a3ea 100644 --- a/web-app/src/containers/Tutorial/LevelPage/Level.tsx +++ b/web-app/src/containers/Tutorial/LevelPage/Level.tsx @@ -39,6 +39,9 @@ const styles = { fontWeight: 'bold' as 'bold', lineHeight: '1.2rem', }, + processes: { + padding: '0 1rem', + }, footer: { height: '36px', backgroundColor: 'black', @@ -101,7 +104,11 @@ const Level = ({ level, onContinue, onLoadSolution, processes }: Props) => {
)} - + {processes.length && ( +
+ +
+ )}
diff --git a/web-app/src/containers/Tutorial/LevelPage/index.tsx b/web-app/src/containers/Tutorial/LevelPage/index.tsx index ecd089c2..4bad6166 100644 --- a/web-app/src/containers/Tutorial/LevelPage/index.tsx +++ b/web-app/src/containers/Tutorial/LevelPage/index.tsx @@ -6,50 +6,50 @@ import * as selectors from '../../../services/selectors' import Level from './Level' interface PageProps { - context: T.MachineContext - send(action: T.Action): void + context: T.MachineContext + send(action: T.Action): void } const LevelSummaryPageContainer = (props: PageProps) => { - const { position, progress } = props.context - - const version = selectors.currentVersion(props.context) - const levelData: G.Level = selectors.currentLevel(props.context) - - const onContinue = (): void => { - props.send({ - type: 'LEVEL_NEXT', - payload: { - LevelId: position.levelId, - }, - }) - } - - const onLoadSolution = (): void => { - props.send({ type: 'STEP_SOLUTION_LOAD' }) - } - - const level: G.Level & { - status: T.ProgressStatus - index: number - steps: Array - } = { - ...levelData, - index: version.data.levels.findIndex((l: G.Level) => l.id === position.levelId), - status: progress.levels[position.levelId] ? 'COMPLETE' : 'ACTIVE', - steps: levelData.steps.map((step: G.Step) => { - // label step status for step component - let status: T.ProgressStatus = 'INCOMPLETE' - if (progress.steps[step.id]) { - status = 'COMPLETE' - } else if (step.id === position.stepId) { - status = 'ACTIVE' - } - return { ...step, status } - }), - } - - return + const { position, progress, processes } = props.context + + const version = selectors.currentVersion(props.context) + const levelData: G.Level = selectors.currentLevel(props.context) + + const onContinue = (): void => { + props.send({ + type: 'LEVEL_NEXT', + payload: { + LevelId: position.levelId, + }, + }) + } + + const onLoadSolution = (): void => { + props.send({ type: 'STEP_SOLUTION_LOAD' }) + } + + const level: G.Level & { + status: T.ProgressStatus + index: number + steps: Array + } = { + ...levelData, + index: version.data.levels.findIndex((l: G.Level) => l.id === position.levelId), + status: progress.levels[position.levelId] ? 'COMPLETE' : 'ACTIVE', + steps: levelData.steps.map((step: G.Step) => { + // label step status for step component + let status: T.ProgressStatus = 'INCOMPLETE' + if (progress.steps[step.id]) { + status = 'COMPLETE' + } else if (step.id === position.stepId) { + status = 'ACTIVE' + } + return { ...step, status } + }), + } + + return } export default LevelSummaryPageContainer diff --git a/web-app/src/services/state/machine.ts b/web-app/src/services/state/machine.ts index 0835dffd..e19ee32f 100644 --- a/web-app/src/services/state/machine.ts +++ b/web-app/src/services/state/machine.ts @@ -20,6 +20,7 @@ export const machine = Machine Date: Sun, 17 Nov 2019 15:26:20 -0800 Subject: [PATCH 10/15] setup command actions --- web-app/src/services/channel/index.ts | 15 +++++--- web-app/src/services/state/actions/command.ts | 37 +++++++++++++++++++ web-app/src/services/state/actions/index.ts | 2 + web-app/src/services/state/machine.ts | 16 +++++++- 4 files changed, 63 insertions(+), 7 deletions(-) create mode 100644 web-app/src/services/state/actions/command.ts diff --git a/web-app/src/services/channel/index.ts b/web-app/src/services/channel/index.ts index dea59cf8..cb131843 100644 --- a/web-app/src/services/channel/index.ts +++ b/web-app/src/services/channel/index.ts @@ -7,6 +7,8 @@ interface ReceivedEvent { } class Channel { + editorSend: (action: Action) => void + machineSend: (action: Action | string) => void constructor() { // setup mock if browser only // @ts-ignore @@ -19,12 +21,9 @@ class Channel { const editor = acquireVsCodeApi() this.editorSend = editor.postMessage - } - public machineSend = (action: Action | string) => { - /* */ - } - public editorSend = (action: Action) => { - /* */ + this.machineSend = () => { + /* machineSend is set asynchronously in the router. see "setMachineSend" */ + } } public setMachineSend = (send: any) => { @@ -53,6 +52,10 @@ class Channel { case 'TEST_FAIL': case 'TEST_RUNNING': case 'TEST_ERROR': + case 'COMMAND_START': + case 'COMMAND_SUCCESS': + case 'COMMAND_FAIL': + case 'COMMAND_ERROR': this.machineSend(action) return default: diff --git a/web-app/src/services/state/actions/command.ts b/web-app/src/services/state/actions/command.ts new file mode 100644 index 00000000..ec4f9d59 --- /dev/null +++ b/web-app/src/services/state/actions/command.ts @@ -0,0 +1,37 @@ +import { assign } from 'xstate' +import * as T from 'typings' + +export default { + // @ts-ignore + commandStart: assign({ + processes: ({ processes }: T.MachineContext, event: T.MachineEvent): T.ProcessEvent[] => { + const { process } = event.payload + const isRunning = processes.find(p => p.title === process.title) + if (!isRunning) { + processes = processes.concat(process) + } + return processes + }, + }), + // @ts-ignore + commandSuccess: assign({ + processes: ({ processes }: T.MachineContext, event: T.MachineEvent): T.ProcessEvent[] => { + const { process } = event.payload + return processes.filter(p => p.title === process.title) + }, + }), + // @ts-ignore + commandFail: assign({ + processes: ({ processes }: T.MachineContext, event: T.MachineEvent): T.ProcessEvent[] => { + const { process } = event.payload + return processes.filter(p => p.title === process.title) + }, + }), + // @ts-ignore + commandError: assign({ + processes: ({ processes }: T.MachineContext, event: T.MachineEvent): T.ProcessEvent[] => { + const { process } = event.payload + return processes.filter(p => p.title === process.title) + }, + }), +} diff --git a/web-app/src/services/state/actions/index.ts b/web-app/src/services/state/actions/index.ts index c2622429..6afae060 100644 --- a/web-app/src/services/state/actions/index.ts +++ b/web-app/src/services/state/actions/index.ts @@ -1,9 +1,11 @@ import editorActions from './editor' import contextActions from './context' import apiActions from './api' +import commandActions from './command' export default { ...editorActions, ...contextActions, ...apiActions, + ...commandActions, } diff --git a/web-app/src/services/state/machine.ts b/web-app/src/services/state/machine.ts index e19ee32f..9e3bd18e 100644 --- a/web-app/src/services/state/machine.ts +++ b/web-app/src/services/state/machine.ts @@ -77,10 +77,24 @@ export const machine = Machine Date: Sun, 17 Nov 2019 15:46:16 -0800 Subject: [PATCH 11/15] setup git actions to send command feedback --- src/actions/setupActions.ts | 22 ++++++++++++++++++---- src/actions/solutionActions.ts | 9 +++++++-- src/channel/index.ts | 6 +++--- 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/actions/setupActions.ts b/src/actions/setupActions.ts index d918168a..b648bd87 100644 --- a/src/actions/setupActions.ts +++ b/src/actions/setupActions.ts @@ -1,3 +1,4 @@ +import * as T from 'typings' import * as G from 'typings/graphql' import * as vscode from 'vscode' import * as git from '../services/git' @@ -6,25 +7,38 @@ import node from '../services/node' import openFiles from './utils/openFiles' import loadWatchers from './utils/loadWatchers' -const runCommands = async (commands: string[]) => { +const runCommands = async (commands: string[], send: (action: T.Action) => void) => { if (!commands.length) { return } for (const command of commands) { + const process = { + title: command, + description: 'Running process', + } + send({ type: 'COMMAND_START', payload: { process: { ...process, status: 'RUNNING' } } }) const { stdout, stderr } = await node.exec(command) if (stderr) { + // TODO: distinguish fail & error console.error(stderr) + send({ type: 'COMMAND_FAIL', payload: { process: { ...process, status: 'FAIL' } } }) + } else { + send({ type: 'COMMAND_SUCCESS', payload: { process: { ...process, status: 'SUCCESS' } } }) } - console.log(`run command: ${command}`, stdout) } } -const setupActions = async (workspaceRoot: vscode.WorkspaceFolder, actions: G.StepActions): Promise => { +const setupActions = async ( + workspaceRoot: vscode.WorkspaceFolder, + actions: G.StepActions, + send: (action: T.Action) => void, // send messages to client +): Promise => { const { commands, commits, files, watchers } = actions // 1. run commits if (commits) { for (const commit of commits) { + // TODO handle git errors await git.loadCommit(commit) } } @@ -36,7 +50,7 @@ const setupActions = async (workspaceRoot: vscode.WorkspaceFolder, actions: G.St loadWatchers(watchers || [], workspaceRoot.uri) // 4. run command - await runCommands(commands || []) + await runCommands(commands || [], send) } export default setupActions diff --git a/src/actions/solutionActions.ts b/src/actions/solutionActions.ts index 28259873..68bc07a4 100644 --- a/src/actions/solutionActions.ts +++ b/src/actions/solutionActions.ts @@ -1,11 +1,16 @@ +import * as T from 'typings' import * as G from 'typings/graphql' import * as vscode from 'vscode' import * as git from '../services/git' import setupActions from './setupActions' -const solutionActions = async (workspaceRoot: vscode.WorkspaceFolder, stepActions: G.StepActions): Promise => { +const solutionActions = async ( + workspaceRoot: vscode.WorkspaceFolder, + stepActions: G.StepActions, + send: (action: T.Action) => void, +): Promise => { await git.clear() - return setupActions(workspaceRoot, stepActions) + return setupActions(workspaceRoot, stepActions, send) } export default solutionActions diff --git a/src/channel/index.ts b/src/channel/index.ts index 72dde390..33ee9570 100644 --- a/src/channel/index.ts +++ b/src/channel/index.ts @@ -93,7 +93,7 @@ class Channel implements Channel { if (data.init) { const setup: G.StepActions | null | undefined = data.init.setup if (setup) { - setupActions(this.workspaceRoot, setup) + setupActions(this.workspaceRoot, setup, this.send) } } @@ -119,11 +119,11 @@ class Channel implements Channel { // load step actions (git commits, commands, open files) case 'SETUP_ACTIONS': vscode.commands.executeCommand(COMMANDS.SET_CURRENT_STEP, action.payload) - setupActions(this.workspaceRoot, action.payload) + setupActions(this.workspaceRoot, action.payload, this.send) return // load solution step actions (git commits, commands, open files) case 'SOLUTION_ACTIONS': - await solutionActions(this.workspaceRoot, action.payload) + await solutionActions(this.workspaceRoot, action.payload, this.send) // run test following solution to update position vscode.commands.executeCommand(COMMANDS.RUN_TEST, action.payload) return From 7f8789caa1004e256e8d6dd026b985c7b32add82 Mon Sep 17 00:00:00 2001 From: shmck Date: Sun, 17 Nov 2019 16:08:29 -0800 Subject: [PATCH 12/15] fix typo bug --- web-app/src/services/channel/index.ts | 12 +++++++----- web-app/src/services/state/machine.ts | 3 ++- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/web-app/src/services/channel/index.ts b/web-app/src/services/channel/index.ts index cb131843..4244e573 100644 --- a/web-app/src/services/channel/index.ts +++ b/web-app/src/services/channel/index.ts @@ -7,8 +7,6 @@ interface ReceivedEvent { } class Channel { - editorSend: (action: Action) => void - machineSend: (action: Action | string) => void constructor() { // setup mock if browser only // @ts-ignore @@ -21,9 +19,13 @@ class Channel { const editor = acquireVsCodeApi() this.editorSend = editor.postMessage - this.machineSend = () => { - /* machineSend is set asynchronously in the router. see "setMachineSend" */ - } + } + + public machineSend = (action: Action | string) => { + /* implemented by `setMachineSend` in router on startup */ + } + public editorSend = (action: Action) => { + /* */ } public setMachineSend = (send: any) => { diff --git a/web-app/src/services/state/machine.ts b/web-app/src/services/state/machine.ts index 9e3bd18e..169b14d1 100644 --- a/web-app/src/services/state/machine.ts +++ b/web-app/src/services/state/machine.ts @@ -78,6 +78,7 @@ export const machine = Machine Date: Sun, 17 Nov 2019 16:08:32 -0800 Subject: [PATCH 13/15] update debugger --- web-app/src/components/Debugger/index.tsx | 34 ++++++++++------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/web-app/src/components/Debugger/index.tsx b/web-app/src/components/Debugger/index.tsx index 0becacf8..356a7ab6 100644 --- a/web-app/src/components/Debugger/index.tsx +++ b/web-app/src/components/Debugger/index.tsx @@ -1,26 +1,22 @@ import * as React from 'react' -import * as G from 'typings/graphql' -import * as CR from 'typings' +import * as T from 'typings' -interface Props { - state: string - tutorial: G.Tutorial - env: CR.Environment - position: CR.Position - progress: CR.Progress - children: React.ReactElement +interface Props extends T.MachineContext { + state: string + children: React.ReactElement } -const Debugger = ({ state, children, env, position, progress, tutorial }: Props) => ( -
-

state: {state}

-

MachineId: {env.machineId}

-

SessionId: {env.sessionId}

-

tutorial: {tutorial ? tutorial.id : 'none'}

-

position: {JSON.stringify(position)}

-

progress: {JSON.stringify(progress)}

- {children} -
+const Debugger = ({ state, children, env, position, progress, processes, tutorial }: Props) => ( +
+

state: {state}

+

MachineId: {env.machineId}

+

SessionId: {env.sessionId}

+

tutorial: {tutorial ? tutorial.id : 'none'}

+

position: {JSON.stringify(position)}

+

progress: {JSON.stringify(progress)}

+

processes: {JSON.stringify(processes)}

+ {children} +
) export default Debugger From 8e8fa5022de8250a1cf2a52bd58964706207d50e Mon Sep 17 00:00:00 2001 From: shmck Date: Sun, 17 Nov 2019 16:36:21 -0800 Subject: [PATCH 14/15] working command tracker --- src/actions/setupActions.ts | 23 +--------------- src/actions/utils/runCommands.ts | 27 +++++++++++++++++++ web-app/src/services/channel/index.ts | 1 - web-app/src/services/state/actions/command.ts | 11 ++------ web-app/src/services/state/machine.ts | 3 --- 5 files changed, 30 insertions(+), 35 deletions(-) create mode 100644 src/actions/utils/runCommands.ts diff --git a/src/actions/setupActions.ts b/src/actions/setupActions.ts index b648bd87..75b6edf4 100644 --- a/src/actions/setupActions.ts +++ b/src/actions/setupActions.ts @@ -2,31 +2,10 @@ import * as T from 'typings' import * as G from 'typings/graphql' import * as vscode from 'vscode' import * as git from '../services/git' -import node from '../services/node' import openFiles from './utils/openFiles' import loadWatchers from './utils/loadWatchers' - -const runCommands = async (commands: string[], send: (action: T.Action) => void) => { - if (!commands.length) { - return - } - for (const command of commands) { - const process = { - title: command, - description: 'Running process', - } - send({ type: 'COMMAND_START', payload: { process: { ...process, status: 'RUNNING' } } }) - const { stdout, stderr } = await node.exec(command) - if (stderr) { - // TODO: distinguish fail & error - console.error(stderr) - send({ type: 'COMMAND_FAIL', payload: { process: { ...process, status: 'FAIL' } } }) - } else { - send({ type: 'COMMAND_SUCCESS', payload: { process: { ...process, status: 'SUCCESS' } } }) - } - } -} +import runCommands from './utils/runCommands' const setupActions = async ( workspaceRoot: vscode.WorkspaceFolder, diff --git a/src/actions/utils/runCommands.ts b/src/actions/utils/runCommands.ts new file mode 100644 index 00000000..08dcd345 --- /dev/null +++ b/src/actions/utils/runCommands.ts @@ -0,0 +1,27 @@ +import * as T from 'typings' +import node from '../../services/node' + +const runCommands = async (commands: string[], send: (action: T.Action) => void) => { + if (!commands.length) { + return + } + for (const command of commands) { + const process = { + title: command, + description: 'Running process', + } + send({ type: 'COMMAND_START', payload: { process: { ...process, status: 'RUNNING' } } }) + let result: { stdout: string; stderr: string } + try { + result = await node.exec(command) + } catch (error) { + console.log(error) + send({ type: 'COMMAND_FAIL', payload: { process: { ...process, status: 'FAIL' } } }) + return + } + console.log(result.stdout) + send({ type: 'COMMAND_SUCCESS', payload: { process: { ...process, status: 'SUCCESS' } } }) + } +} + +export default runCommands diff --git a/web-app/src/services/channel/index.ts b/web-app/src/services/channel/index.ts index 4244e573..c7ece831 100644 --- a/web-app/src/services/channel/index.ts +++ b/web-app/src/services/channel/index.ts @@ -57,7 +57,6 @@ class Channel { case 'COMMAND_START': case 'COMMAND_SUCCESS': case 'COMMAND_FAIL': - case 'COMMAND_ERROR': this.machineSend(action) return default: diff --git a/web-app/src/services/state/actions/command.ts b/web-app/src/services/state/actions/command.ts index ec4f9d59..a56769d6 100644 --- a/web-app/src/services/state/actions/command.ts +++ b/web-app/src/services/state/actions/command.ts @@ -17,21 +17,14 @@ export default { commandSuccess: assign({ processes: ({ processes }: T.MachineContext, event: T.MachineEvent): T.ProcessEvent[] => { const { process } = event.payload - return processes.filter(p => p.title === process.title) + return processes.filter(p => p.title !== process.title) }, }), // @ts-ignore commandFail: assign({ processes: ({ processes }: T.MachineContext, event: T.MachineEvent): T.ProcessEvent[] => { const { process } = event.payload - return processes.filter(p => p.title === process.title) - }, - }), - // @ts-ignore - commandError: assign({ - processes: ({ processes }: T.MachineContext, event: T.MachineEvent): T.ProcessEvent[] => { - const { process } = event.payload - return processes.filter(p => p.title === process.title) + return processes.filter(p => p.title !== process.title) }, }), } diff --git a/web-app/src/services/state/machine.ts b/web-app/src/services/state/machine.ts index 169b14d1..25eae843 100644 --- a/web-app/src/services/state/machine.ts +++ b/web-app/src/services/state/machine.ts @@ -88,9 +88,6 @@ export const machine = Machine Date: Sun, 17 Nov 2019 16:59:25 -0800 Subject: [PATCH 15/15] fix ui issue --- src/actions/utils/runCommands.ts | 2 +- .../src/components/ProcessEvents/index.tsx | 14 ++++---- web-app/src/components/Workspace/index.tsx | 32 +++++++++---------- .../containers/Tutorial/LevelPage/Level.tsx | 2 +- 4 files changed, 24 insertions(+), 26 deletions(-) diff --git a/src/actions/utils/runCommands.ts b/src/actions/utils/runCommands.ts index 08dcd345..07f62ebd 100644 --- a/src/actions/utils/runCommands.ts +++ b/src/actions/utils/runCommands.ts @@ -8,7 +8,7 @@ const runCommands = async (commands: string[], send: (action: T.Action) => void) for (const command of commands) { const process = { title: command, - description: 'Running process', + description: 'Running process...', } send({ type: 'COMMAND_START', payload: { process: { ...process, status: 'RUNNING' } } }) let result: { stdout: string; stderr: string } diff --git a/web-app/src/components/ProcessEvents/index.tsx b/web-app/src/components/ProcessEvents/index.tsx index 79c81db2..52146e44 100644 --- a/web-app/src/components/ProcessEvents/index.tsx +++ b/web-app/src/components/ProcessEvents/index.tsx @@ -2,11 +2,6 @@ import * as React from 'react' import { Message as AlifdMessage } from '@alifd/next' import * as T from 'typings' -interface Process { - title: string - description: string -} - interface Props { processes: T.ProcessEvent[] } @@ -19,12 +14,15 @@ const styles = { } // display a list of active processes -const ProcessEvents = (props: Props) => { +const ProcessEvents = ({ processes }: Props) => { + if (!processes.length) { + return null + } return (
- {props.processes.map(process => ( + {processes.map(process => ( - {process.description}... + {process.description} ))}
diff --git a/web-app/src/components/Workspace/index.tsx b/web-app/src/components/Workspace/index.tsx index 06131a79..da707e70 100644 --- a/web-app/src/components/Workspace/index.tsx +++ b/web-app/src/components/Workspace/index.tsx @@ -1,31 +1,31 @@ import * as React from 'react' interface Props { - children: React.ReactElement + children: React.ReactElement } const resize = () => ({ - minWidth: window.innerWidth - 20, - minHeight: window.innerHeight - 20, + width: window.innerWidth - 20, + height: window.innerHeight - 20, }) const Workspace = ({ children }: Props) => { - const [dimensions, setDimensions] = React.useState(resize()) + const [dimensions, setDimensions] = React.useState(resize()) - // solution for windows getting off size - React.useEffect(() => { - setDimensions(resize()) - }, [window.innerHeight, window.innerWidth]) + // solution for windows getting off size + React.useEffect(() => { + setDimensions(resize()) + }, [window.innerHeight, window.innerWidth]) - const styles = { - page: { - display: 'flex' as 'flex', - margin: 0, - backgroundColor: 'white', - }, - } + const styles = { + page: { + display: 'flex' as 'flex', + margin: 0, + backgroundColor: 'white', + }, + } - return
{children}
+ return
{children}
} export default Workspace diff --git a/web-app/src/containers/Tutorial/LevelPage/Level.tsx b/web-app/src/containers/Tutorial/LevelPage/Level.tsx index 3e46a3ea..0a916bbc 100644 --- a/web-app/src/containers/Tutorial/LevelPage/Level.tsx +++ b/web-app/src/containers/Tutorial/LevelPage/Level.tsx @@ -104,7 +104,7 @@ const Level = ({ level, onContinue, onLoadSolution, processes }: Props) => {
)} - {processes.length && ( + {processes.length > 0 && (