diff --git a/web-app/package-lock.json b/web-app/package-lock.json index 9d1bfc10..d999b592 100644 --- a/web-app/package-lock.json +++ b/web-app/package-lock.json @@ -4807,6 +4807,25 @@ "csstype": "^2.2.0" } }, + "@types/react-addons-css-transition-group": { + "version": "15.0.5", + "resolved": "https://registry.npmjs.org/@types/react-addons-css-transition-group/-/react-addons-css-transition-group-15.0.5.tgz", + "integrity": "sha512-UIJt5HQDOzRI7AOmnGnc2OZA0N3p7r6yMsxZ3T0+dyGPB3zWiKOPKrMkJr9tyuY3kHKPm26GyihcJKNJdMY8CQ==", + "dev": true, + "requires": { + "@types/react": "*", + "@types/react-addons-transition-group": "*" + } + }, + "@types/react-addons-transition-group": { + "version": "15.0.4", + "resolved": "https://registry.npmjs.org/@types/react-addons-transition-group/-/react-addons-transition-group-15.0.4.tgz", + "integrity": "sha512-0S2cKn9OLYr6N36oRH4ybzidkgQ0UGhuvrFvU3tdktJfrx3muu7MgfIWG434wKg7rcysBEfpmQaNpGteEtx6vw==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, "@types/react-color": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/react-color/-/react-color-3.0.1.tgz", @@ -7658,6 +7677,11 @@ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", "dev": true }, + "chain-function": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/chain-function/-/chain-function-1.0.1.tgz", + "integrity": "sha512-SxltgMwL9uCko5/ZCLiyG2B7R9fY4pDZUw7hJ4MhirdjBLosoDqkWABi3XMucddHdLiFJMb7PD2MZifZriuMTg==" + }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -19263,6 +19287,36 @@ "prop-types": "^15.6.2" } }, + "react-addons-css-transition-group": { + "version": "15.6.2", + "resolved": "https://registry.npmjs.org/react-addons-css-transition-group/-/react-addons-css-transition-group-15.6.2.tgz", + "integrity": "sha1-nkN2vPQLUhfRTsaFUwgc7ksIptY=", + "requires": { + "react-transition-group": "^1.2.0" + }, + "dependencies": { + "react-transition-group": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-1.2.1.tgz", + "integrity": "sha512-CWaL3laCmgAFdxdKbhhps+c0HRGF4c+hdM4H23+FI1QBNUyx/AMeIJGWorehPNSaKnQNOAxL7PQmqMu78CDj3Q==", + "requires": { + "chain-function": "^1.0.0", + "dom-helpers": "^3.2.0", + "loose-envify": "^1.3.1", + "prop-types": "^15.5.6", + "warning": "^3.0.0" + } + }, + "warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", + "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", + "requires": { + "loose-envify": "^1.0.0" + } + } + } + }, "react-app-polyfill": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-1.0.6.tgz", diff --git a/web-app/package.json b/web-app/package.json index a5308020..f820a375 100644 --- a/web-app/package.json +++ b/web-app/package.json @@ -38,6 +38,7 @@ "moment": "^2.24.0", "prismjs": "^1.19.0", "react": "^16.13.0", + "react-addons-css-transition-group": "^15.6.2", "react-dom": "^16.13.0", "reselect": "^4.0.0", "typescript": "^3.8.3", @@ -58,6 +59,7 @@ "@types/node": "^13.7.7", "@types/prismjs": "^1.16.0", "@types/react": "^16.9.23", + "@types/react-addons-css-transition-group": "^15.0.5", "@types/react-dom": "^16.9.5", "@typescript-eslint/eslint-plugin": "^2.21.0", "@typescript-eslint/parser": "^2.21.0", diff --git a/web-app/src/components/NewUserExperience/NuxTutorial.tsx b/web-app/src/components/NewUserExperience/NuxTutorial.tsx new file mode 100644 index 00000000..a0a232db --- /dev/null +++ b/web-app/src/components/NewUserExperience/NuxTutorial.tsx @@ -0,0 +1,126 @@ +import React from 'react' +import { Collapse, Icon } from '@alifd/next' +import ReactCSSTransitionGroup from 'react-addons-css-transition-group' +import './transition.css' + +const Panel = Collapse.Panel + +const styles = { + container: { + position: 'relative' as 'relative', + transition: 'all .35s', + }, + header: { + display: 'flex' as 'flex', + backgroundColor: '#6a67ce', + color: 'white', + padding: '0.5rem', + }, + title: { + fontSize: '1rem', + }, + toggle: { + display: 'flex' as 'flex', + alignItems: 'center' as 'center', + width: '1.5rem', + }, +} + +const NewUserExperienceTutorialCollapsible = () => { + const [expandedKeys, setExpandedKeys] = React.useState([]) + return ( + + +
+

Update the editor code and press save to to complete the list of "Tasks".

+
+
+ +
+

+ When you press save in the editor, CodeRoad runs tests to check if you completed the current task and can + continue to the next task. +

+
+

+ Progress is tracked and advanced by using Git in the background. On startup, CodeRoad launches a new local + Git repo. New tasks are loaded as new commits, and your task solution code is automatically saved as the + next Git commit. +

+
+
+ +

You can debug a tutorial in a number of ways:

+
+
    +
  1. + 1. Press save in the editor and use the feedback from failed test messages in the console output. The output + can be found by opening the integrated VSCode terminal, and selecting the tab "Output". Learn more about the + integrated terminal in VSCode at {' '} + + https://code.visualstudio.com/docs/editor/integrated-terminal + + . +
  2. +
    +
  3. + 2. Run the VSCode Debugger located in the left hand icon panel. To start debugging, press the green arrow + button at the top labelled "RUN AND DEBUG". Learn more about debugging with the VSCode Debugger at  + + https://code.visualstudio.com/docs/editor/debugging + + . +
  4. +
    +
  5. + 3. Run the tests in the command line (eg. `npm run test`) and use the output from the tests to debug. Feel + free to use the integrated VScode terminal noted above or another terminal with the project working + directory open. . +
  6. +
+
+ +

A few tips to help you if you get stuck.

+
    +
  1. + Read the tests. The tests can be found in the test directory and can be read in detail to help you + understand what's failing. +
  2. +
    +
  3. + Load the solution. Each step in CodeRoad is stored as a Git commit - including the solution. Load the + solution by pressing the help icon under the current task, and select the option "load solution". +
  4. +
+
+ + We'd love to hear your comments, requests, issues, questions - reach out at{' '} + coderoadapp@gmail.com. + +
+ ) +} + +interface Props { + css?: React.CSSProperties +} + +const NewUserExperienceTutorial = (props: Props) => { + const [isOpen, setIsOpen] = React.useState(false) + const onToggle = () => { + setIsOpen(!isOpen) + } + return ( +
+
+ {isOpen ? : } + Help +
+ + {isOpen && } + +
+ ) +} + +export default NewUserExperienceTutorial diff --git a/web-app/src/components/NewUserExperience/transition.css b/web-app/src/components/NewUserExperience/transition.css new file mode 100644 index 00000000..b2fae318 --- /dev/null +++ b/web-app/src/components/NewUserExperience/transition.css @@ -0,0 +1,21 @@ +.slide-enter { + max-height: 0; + overflow: hidden; +} + +.slide-enter.slide-enter-active { + max-height: 100rem; + overflow: auto; + transition: max-height 500ms ease-in; +} + +.slide-leave { + max-height: 100rem; + overflow: auto; +} + +.slide-leave.slide-leave-active { + max-height: 0; + overflow: hidden; + transition: max-height 300ms ease-out; +} diff --git a/web-app/src/containers/Tutorial/LevelPage/Level.tsx b/web-app/src/containers/Tutorial/LevelPage/Level.tsx index df032780..8b24b24d 100644 --- a/web-app/src/containers/Tutorial/LevelPage/Level.tsx +++ b/web-app/src/containers/Tutorial/LevelPage/Level.tsx @@ -5,6 +5,7 @@ import { css, jsx } from '@emotion/core' import Button from '../../../components/Button' import Markdown from '../../../components/Markdown' import ProcessMessages from '../../../components/ProcessMessages' +import NuxTutorial from '../../../components/NewUserExperience/NuxTutorial' import Step from './Step' const styles = { @@ -14,7 +15,7 @@ const styles = { display: 'flex' as 'flex', flexDirection: 'column' as 'column', padding: 0, - paddingBottom: '4.5rem', + paddingBottom: '5rem', height: 'auto', width: '100%', }, @@ -42,6 +43,12 @@ const styles = { }, processes: { padding: '0 1rem', + position: 'fixed' as 'fixed', + bottom: '4rem', + left: 0, + right: 0, + }, + nux: { position: 'fixed' as 'fixed', bottom: '2rem', left: 0, @@ -129,6 +136,10 @@ const Level = ({ level, onContinue, onLoadSolution, processes, testStatus }: Pro )} +
+ +
+
{typeof level.index === 'number' ? `${level.index + 1}. ` : ''} diff --git a/web-app/stories/NewUserExperience.stories.tsx b/web-app/stories/NewUserExperience.stories.tsx new file mode 100644 index 00000000..22945b5c --- /dev/null +++ b/web-app/stories/NewUserExperience.stories.tsx @@ -0,0 +1,22 @@ +import { storiesOf } from '@storybook/react' +import React from 'react' +import { css, jsx } from '@emotion/core' +import SideBarDecorator from './utils/SideBarDecorator' +import NewUserExperienceTutorial from '../src/components/NewUserExperience/NuxTutorial' + +const styles = { + container: { + position: 'absolute', + bottom: 0, + left: 0, + right: 0, + }, +} + +storiesOf('NewUserExperience', module) + .addDecorator(SideBarDecorator) + .add('NUXTutorial', () => ( +
+ +
+ ))