feat: react challenges (#16099)

* chore(packages): Update redux utils

* feat(Panes): Invert control of panes map creation

* feat(Modern): Add view

* feat(Panes): Decouple panes from Challenges

* fix(Challenges): Decouple challenge views from panes map

* fix(Challenge/views): PanesMap => mapStateToPanesMap

This clarifies what these functions are doing

* fix(Challenges): Add view type

* fix(Panes): Remove unneeded panes container

* feat(Panes): Invert control of pane content render

This decouples the Panes from the content they render, allowing for
greater flexibility.

* feat(Modern): Add side panel

This is common between modern and classic

* feat(seed): Array to string file content

* fix(files): Modern files should be polyvinyls

* feat(Modern): Create editors per file

* fix(seed/React): Incorrect keyfile name

* feat(Modern): Highligh jsx correctly

This adds highlighting for jsx. Unfortunately, this disables linting for
non-javascript files as jshint will only work for those

* feat(rechallenge): Add jsx ext to babel transformer

* feat(seed): Normalize challenge files head/tail/content

* refactor(rechallenge/build): Rename function

* fix(code-storage): Pull in files from localStorage

* feat(Modern/React): Add Enzyme to test runner

This enables testing of React challenges

* feat(Modern): Add submission type

* refactor(Panes): Rename panes map update action
This commit is contained in:
Berkeley Martinez
2017-11-29 15:44:51 -08:00
committed by Quincy Larson
parent 803f9895dd
commit b4084f94ff
2 changed files with 22 additions and 4 deletions

View File

@ -3,6 +3,11 @@
"order": 5, "order": 5,
"time": "5 hours", "time": "5 hours",
"helpRoom": "Help", "helpRoom": "Help",
"required": [
{
"src": "https://cdnjs.cloudflare.com/ajax/libs/react/16.1.1/umd/react.development.js"
}
],
"challenges": [ "challenges": [
{ {
"id": "587d7dbc367417b2b2512bb1", "id": "587d7dbc367417b2b2512bb1",
@ -17,7 +22,7 @@
], ],
"files": { "files": {
"indexjsx": { "indexjsx": {
"key": "indexjxs", "key": "indexjsx",
"ext": "jsx", "ext": "jsx",
"name": "index", "name": "index",
"contents": [ "contents": [
@ -28,11 +33,11 @@
} }
}, },
"tests": [ "tests": [
"assert(Enzyme.shallow(jsx).type === 'h1', 'message: The constant JSX should return an <code>h1</code> element.');", "assert(Enzyme.shallow(jsx).type() === 'h1', 'message: The constant JSX should return an <code>h1</code> element.');",
"assert(Enzyme.shallow(jsx).children() === 'Hello JSX!', 'message: The <code>h1</code> tag should include the text <code>Hello JSX!</code>');" "assert(Enzyme.shallow(jsx).contains( 'Hello JSX!'), 'message: The <code>h1</code> tag should include the text <code>Hello JSX!</code>');"
], ],
"type": "modern", "type": "modern",
"isRequired": true, "isRequired": false,
"translations": {} "translations": {}
} }
] ]

View File

@ -23,6 +23,8 @@ var createChallenges =
var Block = app.models.Block; var Block = app.models.Block;
var destroyBlocks = Observable.fromNodeCallback(Block.destroyAll, Block); var destroyBlocks = Observable.fromNodeCallback(Block.destroyAll, Block);
var createBlocks = Observable.fromNodeCallback(Block.create, Block); var createBlocks = Observable.fromNodeCallback(Block.create, Block);
const arrToString = arr =>
Array.isArray(arr) ? arr.join('\n') : _.toString(arr);
Observable.combineLatest( Observable.combineLatest(
destroyChallenges(), destroyChallenges(),
@ -82,6 +84,17 @@ Observable.combineLatest(
) )
); );
if (challenge.files) {
challenge.files = _.reduce(challenge.files, (map, file) => {
map[file.key] = {
...file,
head: arrToString(file.head),
contents: arrToString(file.contents),
tail: arrToString(file.tail)
};
return map;
}, {});
}
challenge.fileName = fileName; challenge.fileName = fileName;
challenge.helpRoom = helpRoom; challenge.helpRoom = helpRoom;
challenge.order = order; challenge.order = order;