├── .nvmrc ├── __mocks__ └── fileMock.js ├── .dockerignore ├── src ├── styles │ ├── global.scss │ ├── golden-layout │ │ ├── index.css │ │ ├── custom.css │ │ ├── light.css │ │ └── base.css │ ├── mixins.scss │ └── variables.scss ├── helpers │ ├── noop.ts │ ├── polyfill.ts │ ├── pick.ts │ ├── pluginManager.ts │ ├── wrap.tsx │ ├── uuid.ts │ ├── hub.ts │ ├── epics.ts │ ├── baseExercise.ts │ ├── output.ts │ └── plot.ts ├── config.ts ├── containers │ ├── __snapshots__ │ │ ├── App.spec.tsx.snap │ │ ├── Plot.spec.tsx.snap │ │ ├── Editor.spec.tsx.snap │ │ ├── Console.spec.tsx.snap │ │ ├── Footer.spec.tsx.snap │ │ ├── BackendStatus.spec.tsx.snap │ │ ├── SolutionEditor.spec.tsx.snap │ │ ├── GlobalAlerts.spec.tsx.snap │ │ ├── FeedbackMessage.spec.tsx.snap │ │ └── RestartSessionButton.spec.tsx.snap │ ├── GlobalAlerts.ts │ ├── App.spec.tsx │ ├── Plot.spec.tsx │ ├── Editor.spec.tsx │ ├── Footer.spec.tsx │ ├── Console.spec.tsx │ ├── GlobalAlerts.spec.tsx │ ├── BackendStatus.spec.tsx │ ├── SolutionEditor.spec.tsx │ ├── FeedbackMessage.spec.tsx │ ├── RestartSessionButton.spec.tsx │ ├── App.tsx │ ├── FeedbackMessage.tsx │ ├── SolutionEditor.tsx │ ├── RestartSessionButton.tsx │ ├── Terminal.tsx │ ├── BackendStatus.tsx │ ├── Editor.tsx │ ├── Plot.tsx │ ├── Footer.tsx │ └── Console.ts ├── components │ ├── __snapshots__ │ │ ├── AlertList.spec.tsx.snap │ │ ├── _meta.spec.tsx.snap │ │ ├── Placeholder.spec.tsx.snap │ │ ├── Button.spec.tsx.snap │ │ ├── Alert.spec.tsx.snap │ │ ├── RestartSessionButton.spec.tsx.snap │ │ └── Footer.spec.tsx.snap │ ├── Terminal.module.scss │ ├── Placeholder.tsx │ ├── Button.spec.tsx │ ├── Footer.spec.tsx │ ├── AlertList.spec.tsx │ ├── Placeholder.spec.tsx │ ├── _meta.spec.tsx │ ├── RestartSessionButton.spec.tsx │ ├── AlertList.tsx │ ├── App │ │ ├── index.spec.tsx │ │ ├── __snapshots__ │ │ │ └── index.spec.tsx.snap │ │ ├── index.module.scss │ │ └── index.tsx │ ├── Placeholder.module.scss │ ├── Alert.spec.tsx │ ├── Footer.module.scss │ ├── RestartSessionButton.tsx │ ├── Button.tsx │ ├── Alert.tsx │ ├── Alert.module.scss │ ├── Button.module.scss │ ├── Footer.tsx │ └── Terminal.tsx ├── index.spec.ts ├── test-setup.ts ├── redux │ ├── store.ts │ ├── index.spec.ts │ ├── autocomplete.spec.ts │ ├── index.ts │ ├── autocomplete.ts │ ├── view.ts │ ├── output.ts │ ├── exercise.ts │ ├── backend-session.spec.ts │ └── backend-session.ts ├── index.ts ├── images │ ├── datacamp-logo.svg │ └── github-icon.svg ├── boot.spec.tsx ├── boot.tsx └── index.d.ts ├── webpack.config.js ├── .npmrc ├── .eslintrc.js ├── .gitignore ├── tslint.json ├── .editorconfig ├── stylelint.config.js ├── configs ├── config.js ├── webpack.serve.js ├── webpack.dev.js ├── webpack.prod.js └── webpack.common.js ├── vendor └── @datacamp │ ├── ui-backend-status │ ├── index.d.ts │ └── lib │ │ └── style.css │ ├── ui-plot │ ├── index.d.ts │ └── lib │ │ └── style.css │ ├── ui-console │ ├── lib │ │ └── style.css │ └── index.d.ts │ ├── ui-editor │ ├── lib │ │ └── style.css │ └── index.d.ts │ └── plugin-manager │ └── index.js ├── tsconfig.json ├── .circleci └── config.yml └── package.json /.nvmrc: -------------------------------------------------------------------------------- 1 | v8.9.1 -------------------------------------------------------------------------------- /__mocks__/fileMock.js: -------------------------------------------------------------------------------- 1 | module.exports = "test-file-stub"; 2 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | node_modules 3 | bower_components 4 | 5 | -------------------------------------------------------------------------------- /src/styles/global.scss: -------------------------------------------------------------------------------- 1 | @import "./variables.scss"; 2 | @import "./mixins.scss"; 3 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = env => require(`./configs/webpack.${env}.js`); 2 | -------------------------------------------------------------------------------- /src/helpers/noop.ts: -------------------------------------------------------------------------------- 1 | const noop = (...vars: any[]): void => undefined; 2 | 3 | export default noop; 4 | -------------------------------------------------------------------------------- /src/helpers/polyfill.ts: -------------------------------------------------------------------------------- 1 | if (!(global as any)._babelPolyfill) { 2 | require("babel-polyfill"); 3 | } 4 | -------------------------------------------------------------------------------- /src/styles/golden-layout/index.css: -------------------------------------------------------------------------------- 1 | @import "./base.css"; 2 | @import "./light.css"; 3 | @import "./custom.css"; 4 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | //registry.npmjs.org/:_authToken=${NPM_TOKEN} 2 | scope=datacamp 3 | @datacamp:registry=https://registry.npmjs.org/ -------------------------------------------------------------------------------- /src/config.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | env: process.env.NODE_ENV, 3 | urls: { 4 | multiplexer: "https://multiplexer-prod.datacamp.com", 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /src/containers/__snapshots__/App.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`App should match snapshot 1`] = ``; 4 | -------------------------------------------------------------------------------- /src/containers/__snapshots__/Plot.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Plot should match snapshot 1`] = ``; 4 | -------------------------------------------------------------------------------- /src/components/__snapshots__/AlertList.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`AlertList should match snapshot 1`] = `
`; 4 | -------------------------------------------------------------------------------- /src/containers/__snapshots__/Editor.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Editor should match snapshot 1`] = ``; 4 | -------------------------------------------------------------------------------- /src/components/Terminal.module.scss: -------------------------------------------------------------------------------- 1 | .terminal { 2 | height: 100%; 3 | 4 | :global .xterm { 5 | padding: 10px; 6 | height: 100%; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/components/__snapshots__/_meta.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Enzyme should create and match snapshot 1`] = `
`; 4 | -------------------------------------------------------------------------------- /src/containers/__snapshots__/Console.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Console should match snapshot 1`] = ``; 4 | -------------------------------------------------------------------------------- /src/containers/__snapshots__/Footer.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Footer should match snapshot 1`] = ``; 4 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ["prettier", "prettier/react"], 3 | plugins: ["prettier"], 4 | rules: { 5 | "prettier/prettier": "error", 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/containers/__snapshots__/BackendStatus.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`BackendStatus should match snapshot 1`] = ``; 4 | -------------------------------------------------------------------------------- /src/containers/__snapshots__/SolutionEditor.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`SolutionEditor should match snapshot 1`] = ``; 4 | -------------------------------------------------------------------------------- /src/containers/__snapshots__/GlobalAlerts.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`GlobalAlerts should match snapshot 1`] = ``; 4 | -------------------------------------------------------------------------------- /src/containers/__snapshots__/FeedbackMessage.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`FeedbackMessage should match snapshot 1`] = ``; 4 | -------------------------------------------------------------------------------- /src/index.spec.ts: -------------------------------------------------------------------------------- 1 | import * as index from "./index"; 2 | 3 | describe("index", () => { 4 | test("should export init function", () => { 5 | expect(index.init).toBeInstanceOf(Function); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /src/containers/__snapshots__/RestartSessionButton.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`RestartSessionButton should match snapshot 1`] = ``; 4 | -------------------------------------------------------------------------------- /src/styles/golden-layout/custom.css: -------------------------------------------------------------------------------- 1 | .lm_header .lm_tabs { 2 | display: flex; 3 | width: 100%; 4 | overflow-x: scroll; 5 | padding-bottom: 30px; 6 | } 7 | 8 | .lm_header { 9 | overflow: hidden !important; 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | build/ 4 | *.log* 5 | npm-debug.log 6 | ecs_task.json 7 | dist/ 8 | public/ 9 | .idea 10 | .awcache 11 | 12 | *.css.d.ts 13 | *.scss.d.ts 14 | src/**/*.js 15 | src/**/*.js.map 16 | .vscode -------------------------------------------------------------------------------- /src/components/__snapshots__/Placeholder.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Placeholder should match snapshot 1`] = ` 4 |
7 | 8 |
9 | `; 10 | -------------------------------------------------------------------------------- /src/components/__snapshots__/Button.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Button should match snapshot 1`] = ` 4 | 20 |
21 | `; 22 | -------------------------------------------------------------------------------- /configs/config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | const env = process.env.NODE_ENV || "production"; 4 | const port = process.env.PORT || 8888; 5 | const root = path.resolve(__dirname, ".."); 6 | const outputPath = path.resolve(root, "dist"); 7 | const vendorOutputPath = path.resolve(root, "lib"); 8 | 9 | module.exports = { 10 | env, 11 | root, 12 | outputPath, 13 | vendorOutputPath, 14 | isProductionMode: () => env === "production", 15 | port: port, 16 | host: "localhost", 17 | }; 18 | -------------------------------------------------------------------------------- /vendor/@datacamp/ui-backend-status/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module "@datacamp/ui-backend-status" { 2 | import { Component, Props } from "react"; 3 | 4 | interface IBackendStatusProps extends Props { 5 | className?: string; 6 | isInitSession?: boolean; 7 | language?: string; 8 | runtimeConfig?: string; 9 | status: { 10 | code: string; 11 | text: string; 12 | }; 13 | } 14 | 15 | export default class BackendStatus extends Component {} 16 | } 17 | -------------------------------------------------------------------------------- /src/redux/store.ts: -------------------------------------------------------------------------------- 1 | import { Map as hashMap } from "immutable"; 2 | import { compose, createStore, GenericStoreEnhancer } from "redux"; 3 | import reducer, { State } from "./"; 4 | 5 | export default (storeEnhancer: GenericStoreEnhancer) => { 6 | const store = createStore(reducer, new State(), storeEnhancer); 7 | 8 | if (module.hot) { 9 | // Enable webpack hot module replacement for reducers 10 | module.hot.accept("./", () => store.replaceReducer(reducer)); 11 | } 12 | 13 | return store; 14 | }; 15 | -------------------------------------------------------------------------------- /src/helpers/pluginManager.ts: -------------------------------------------------------------------------------- 1 | import Multiplexer, { 2 | PLUGIN_NAME as MUX_NAME, 3 | } from "@datacamp/multiplexer-client"; 4 | import PluginManager from "@datacamp/plugin-manager"; 5 | 6 | // create a singleton to handle all the plugins 7 | const pm = new PluginManager(); 8 | 9 | export const getMux = (muxName: string) => { 10 | if (!muxName) { 11 | throw new TypeError(`Expected muxName to be non-falsy, got "${muxName}"`); 12 | } 13 | 14 | return pm.get(MUX_NAME + muxName) as Multiplexer.AsyncSession; 15 | }; 16 | 17 | export default pm; 18 | -------------------------------------------------------------------------------- /src/components/AlertList.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { IAlert } from "../redux/view"; 4 | import Alert from "./Alert"; 5 | 6 | export interface IAlertListProps extends React.Props { 7 | alerts: IAlert[]; 8 | } 9 | 10 | export class AlertList extends React.Component { 11 | public static defaultProps: Partial = { 12 | alerts: [], 13 | }; 14 | 15 | public render() { 16 | return
{this.props.alerts.map(alert => )}
; 17 | } 18 | } 19 | 20 | export default AlertList; 21 | -------------------------------------------------------------------------------- /src/components/App/index.spec.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { shallow } from "enzyme"; 3 | import { createStore } from "redux"; 4 | 5 | import reducer, { State } from "../../redux"; 6 | 7 | import createApp from "./"; 8 | 9 | describe("App", () => { 10 | const store = createStore(reducer, new State()); 11 | const App = createApp(store); 12 | 13 | it("should exist", () => { 14 | expect(App).toBeDefined(); 15 | }); 16 | 17 | it("should match snapshot", () => { 18 | const tree = shallow(); 19 | expect(tree).toMatchSnapshot(); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/helpers/wrap.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { Provider } from "react-redux"; 3 | import { Store } from "redux"; 4 | 5 | import { State } from "../redux"; 6 | 7 | type HOC

= React.ComponentClass

| React.SFC

; 8 | 9 | export function wrap

(MyComponent: HOC

, store: Store) { 10 | class Wrapped extends React.Component

{ 11 | public render() { 12 | return ( 13 | 14 | 15 | 16 | ); 17 | } 18 | } 19 | 20 | return Wrapped; 21 | } 22 | 23 | export default wrap; 24 | -------------------------------------------------------------------------------- /src/components/Placeholder.module.scss: -------------------------------------------------------------------------------- 1 | @import "global.scss"; 2 | 3 | .placeholder { 4 | /* stylelint-disable declaration-block-no-duplicate-properties, declaration-block-no-shorthand-property-overrides */ 5 | @include preventive-overrides; 6 | outline: 1px solid darken($greySubtle, 5%) !important; 7 | background: #fff; 8 | /* stylelint-enable */ 9 | margin: 0; 10 | background-size: auto 80px !important; 11 | position: relative; 12 | min-height: 300px; 13 | display: flex; 14 | align-items: center; 15 | justify-content: center; 16 | } 17 | 18 | .placeholder > code, 19 | .placeholder > div, 20 | .placeholder > p { 21 | display: none; 22 | } 23 | -------------------------------------------------------------------------------- /src/containers/App.spec.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { shallow } from "enzyme"; 3 | import { createStore } from "redux"; 4 | import { Provider } from "react-redux"; 5 | 6 | import reducer, { State } from "../redux"; 7 | 8 | import App from "./App"; 9 | 10 | describe("App", () => { 11 | const store = createStore(reducer, new State()); 12 | 13 | it("should exist", () => { 14 | expect(App).toBeDefined(); 15 | }); 16 | 17 | it("should match snapshot", () => { 18 | const tree = shallow( 19 | 20 | 21 | 22 | ); 23 | expect(tree).toMatchSnapshot(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/containers/Plot.spec.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { shallow } from "enzyme"; 3 | import { createStore } from "redux"; 4 | import { Provider } from "react-redux"; 5 | 6 | import reducer, { State } from "../redux"; 7 | 8 | import Plot from "./Plot"; 9 | 10 | describe("Plot", () => { 11 | const store = createStore(reducer, new State()); 12 | 13 | it("should exist", () => { 14 | expect(Plot).toBeDefined(); 15 | }); 16 | 17 | it("should match snapshot", () => { 18 | const tree = shallow( 19 | 20 | 21 | 22 | ); 23 | expect(tree).toMatchSnapshot(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/containers/Editor.spec.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { shallow } from "enzyme"; 3 | import { createStore } from "redux"; 4 | import { Provider } from "react-redux"; 5 | 6 | import reducer, { State } from "../redux"; 7 | 8 | import Editor from "./Editor"; 9 | 10 | describe("Editor", () => { 11 | const store = createStore(reducer, new State()); 12 | 13 | it("should exist", () => { 14 | expect(Editor).toBeDefined(); 15 | }); 16 | 17 | it("should match snapshot", () => { 18 | const tree = shallow( 19 | 20 | 21 | 22 | ); 23 | expect(tree).toMatchSnapshot(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/containers/Footer.spec.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { shallow } from "enzyme"; 3 | import { createStore } from "redux"; 4 | import { Provider } from "react-redux"; 5 | 6 | import reducer, { State } from "../redux"; 7 | 8 | import Footer from "./Footer"; 9 | 10 | describe("Footer", () => { 11 | const store = createStore(reducer, new State()); 12 | 13 | it("should exist", () => { 14 | expect(Footer).toBeDefined(); 15 | }); 16 | 17 | it("should match snapshot", () => { 18 | const tree = shallow( 19 | 20 |