diff --git a/locales/en/translation.json b/locales/en/translation.json
index bb5f633d1a..a0a8393b4b 100644
--- a/locales/en/translation.json
+++ b/locales/en/translation.json
@@ -4,6 +4,8 @@
"export-gist": "Export Gist",
"export-repo": "Export Repo",
"share-to-classroom": "Share To Classroom",
+ "add-instructions": "Add Instructions",
+ "edit-instructions": "Edit Instructions",
"libraries": "Libraries",
"load-project": "Your Projects",
"new-project": "New Project",
@@ -60,7 +62,11 @@
"css": "CSS",
"javascript": "JS"
},
- "output": "Output"
+ "output": "Output",
+ "instructions": {
+ "cancel": "Cancel",
+ "save": "Save"
+ }
}
},
"utility": {
diff --git a/src/actions/index.js b/src/actions/index.js
index 61efc20fe1..3998f368b8 100644
--- a/src/actions/index.js
+++ b/src/actions/index.js
@@ -15,6 +15,7 @@ import {
unhideComponent,
toggleComponent,
updateProjectSource,
+ updateProjectInstructions,
} from './projects';
import {
@@ -31,6 +32,8 @@ import {
toggleEditorTextSize,
toggleTopBarMenu,
closeTopBarMenu,
+ startEditingInstructions,
+ cancelEditingInstructions,
} from './ui';
import {
@@ -67,6 +70,7 @@ export {
createSnapshot,
changeCurrentProject,
updateProjectSource,
+ updateProjectInstructions,
toggleLibrary,
userAuthenticated,
userLoggedOut,
@@ -92,6 +96,8 @@ export {
toggleEditorTextSize,
toggleTopBarMenu,
closeTopBarMenu,
+ startEditingInstructions,
+ cancelEditingInstructions,
logIn,
logOut,
evaluateConsoleEntry,
diff --git a/src/actions/projects.js b/src/actions/projects.js
index e9a662bbc0..0fc760e053 100644
--- a/src/actions/projects.js
+++ b/src/actions/projects.js
@@ -18,6 +18,11 @@ export const updateProjectSource = createAction(
(_projectKey, _language, _newValue, timestamp = Date.now()) => ({timestamp}),
);
+export const updateProjectInstructions = createAction(
+ 'UPDATE_PROJECT_INSTRUCTIONS',
+ (projectKey, newValue) => ({projectKey, newValue}),
+);
+
export const toggleLibrary = createAction(
'TOGGLE_LIBRARY',
(projectKey, libraryKey) => ({projectKey, libraryKey}),
diff --git a/src/actions/ui.js b/src/actions/ui.js
index 9a39434445..09cbb179ff 100644
--- a/src/actions/ui.js
+++ b/src/actions/ui.js
@@ -58,3 +58,11 @@ export const toggleTopBarMenu = createAction(
export const closeTopBarMenu = createAction(
'CLOSE_TOP_BAR_MENU',
);
+
+export const startEditingInstructions = createAction(
+ 'START_EDITING_INSTRUCTIONS',
+);
+
+export const cancelEditingInstructions = createAction(
+ 'CANCEL_EDITING_INSTRUCTIONS',
+);
diff --git a/src/components/Instructions.jsx b/src/components/Instructions.jsx
index 32068f8e30..050416f68c 100644
--- a/src/components/Instructions.jsx
+++ b/src/components/Instructions.jsx
@@ -1,9 +1,17 @@
import React from 'react';
import PropTypes from 'prop-types';
import {toReact as markdownToReact} from '../util/markdown';
+import InstructionsEditor from './InstructionsEditor';
-export default function Instructions({instructions, isOpen}) {
- if (!instructions || !isOpen) {
+export default function Instructions({
+ instructions,
+ isEditing,
+ isOpen,
+ projectKey,
+ onCancelEditing,
+ onSaveChanges,
+}) {
+ if (!isEditing && !instructions || !isOpen) {
return null;
}
@@ -11,14 +19,27 @@ export default function Instructions({instructions, isOpen}) {
-
- {markdownToReact(instructions)}
-
+ {
+ isEditing ?
+
:
+
+ {markdownToReact(instructions)}
+
+ }
);
}
Instructions.propTypes = {
instructions: PropTypes.string.isRequired,
+ isEditing: PropTypes.bool.isRequired,
isOpen: PropTypes.bool.isRequired,
+ projectKey: PropTypes.string.isRequired,
+ onCancelEditing: PropTypes.func.isRequired,
+ onSaveChanges: PropTypes.func.isRequired,
};
diff --git a/src/components/InstructionsEditor.jsx b/src/components/InstructionsEditor.jsx
new file mode 100644
index 0000000000..7a678449bd
--- /dev/null
+++ b/src/components/InstructionsEditor.jsx
@@ -0,0 +1,69 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import {t} from 'i18next';
+import bindAll from 'lodash/bindAll';
+
+export default class InstructionsEditor extends React.Component {
+ constructor() {
+ super();
+ bindAll(this, '_handleCancelEditing', '_handleSaveChanges', '_ref');
+ }
+
+ _handleCancelEditing() {
+ this.props.onCancelEditing();
+ }
+
+ _handleSaveChanges() {
+ const newValue = this._editor.value.trim();
+ this.props.onSaveChanges(this.props.projectKey, newValue);
+ }
+
+ _ref(editorElement) {
+ this._editor = editorElement;
+ }
+
+ render() {
+ return (
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+InstructionsEditor.propTypes = {
+ instructions: PropTypes.string.isRequired,
+ projectKey: PropTypes.string.isRequired,
+ onCancelEditing: PropTypes.func.isRequired,
+ onSaveChanges: PropTypes.func.isRequired,
+};
diff --git a/src/components/TopBar/HamburgerMenu.jsx b/src/components/TopBar/HamburgerMenu.jsx
index 11281eef34..9bc999b96e 100644
--- a/src/components/TopBar/HamburgerMenu.jsx
+++ b/src/components/TopBar/HamburgerMenu.jsx
@@ -12,6 +12,8 @@ const HamburgerMenu = createMenu({
name: 'hamburger',
renderItems({
+ hasInstructions,
+ isEditingInstructions,
isExperimental,
isGistExportInProgress,
isRepoExportInProgress,
@@ -20,6 +22,7 @@ const HamburgerMenu = createMenu({
onExportGist,
onExportRepo,
onExportToClassroom,
+ onStartEditingInstructions,
}) {
return tap([], (items) => {
items.push(
@@ -31,6 +34,20 @@ const HamburgerMenu = createMenu({
,
);
+ items.push(
+ ,
+ );
+
if (isUserAuthenticated) {
items.push(