Skip to content

Commit

Permalink
Merge branch 'Simon-Initiative:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
dtiwarATS authored Sep 8, 2023
2 parents a0e8e63 + edba476 commit 54316a7
Show file tree
Hide file tree
Showing 92 changed files with 4,982 additions and 698 deletions.
5 changes: 5 additions & 0 deletions assets/__mocks__/react-markdown.mock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
ReactMarkdown: function ({ children }) {
return children;
}
}
6 changes: 5 additions & 1 deletion assets/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
@import 'text';
@import 'button';
@import 'table';
@import "markdown-editor";


/**
* Automatically style all links with blue text and underline on hover.
Expand All @@ -18,10 +20,12 @@
*/
a,
a.btn.btn-link {
/* prettier-ignore */
@apply text-blue-500 hover:text-blue-600 hover:underline;
}

a.btn {
/* prettier-ignore */
@apply hover:no-underline;
}

Expand Down Expand Up @@ -63,4 +67,4 @@ a.download::after {

body {
scrollbar-color: transparent;
}
}
20 changes: 20 additions & 0 deletions assets/css/markdown-editor.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
@import "@uiw/react-md-editor/markdown-editor";
@import "@uiw/react-markdown-preview/markdown";

.wmde-markdown-color,
textarea.w-md-editor-text-input {
/* Really want a fixed-width font while editing */
--md-editor-font-family: Consolas, "Courier New", monospace;
}

.wmde-markdown ul {
list-style: disc;
}

.wmde-markdown ol {
list-style: decimal;
}

.w-md-editor .w-md-editor-content {
min-height: 2.1rem;
}
6 changes: 6 additions & 0 deletions assets/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ module.exports = {
'^utils/(.*)': '<rootDir>/src/utils/$1',
'\\.[s]css': 'identity-obj-proxy',
'monaco-editor': '<rootDir>/__mocks__/monaco.mock.js',

/* react-markdown and rehype are esm modules that don't play nice with jest so we mock them out.
this does mean you can't write tests for them, but currently we don't have any. */
'react-markdown': '<rootDir>/__mocks__/react-markdown.mock.js',
'rehype': '<rootDir>/__mocks__/react-markdown.mock.js',

// necessary for jest to handle non-js file imports by mapping to an empty module
'\\.(css|scss|wav)$': '<rootDir>/__mocks__/empty.mock.js',
},
Expand Down
2 changes: 2 additions & 0 deletions assets/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"@stripe/stripe-js": "^1.18.0",
"@tailwindcss/container-queries": "^0.1.0",
"@tailwindui/react": "^0.1.1",
"@uiw/react-md-editor": "^3.23.5",
"@uiw/react-monacoeditor": "^3.5.2",
"ace-builds": "^1.4.13",
"ajv": "^8.11.0",
Expand All @@ -59,6 +60,7 @@
"json-rules-engine": "^6.0.1",
"json-schema": "^0.4.0",
"jsonpath": "^1.1.1",
"marked": "^7.0.4",
"mathlive": "^0.77.0",
"monaco-editor": "^0.32.1",
"nprogress": "^0.2.0",
Expand Down
11 changes: 8 additions & 3 deletions assets/src/apps/page-editor/PageEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { ActivityModelSchema, Undoable as ActivityUndoable } from 'components/ac
import { EditorUpdate as ActivityEditorUpdate } from 'components/activity/InlineActivityEditor';
import { PersistenceStatus } from 'components/content/PersistenceStatus';
import { TitleBar } from 'components/content/TitleBar';
import { setDefaultEditor } from 'components/editing/markdown_editor/markdown_util';
import { AlternativesContextProvider } from 'components/hooks/useAlternatives';
import { Banner } from 'components/messages/Banner';
import { ModalDisplay } from 'components/modal/ModalDisplay';
Expand All @@ -25,6 +26,7 @@ import { Objective } from 'data/content/objective';
import {
ActivityMap,
ActivityReference,
EditorType,
ResourceContent,
ResourceContext,
StructuredContent,
Expand Down Expand Up @@ -53,6 +55,7 @@ export interface PageEditorProps extends ResourceContext {
activities: ActivityMap;
featureFlags: FeatureFlags;
appsignalKey: string | null;
defaultEditor: EditorType;
onLoadPreferences: () => void;
}

Expand Down Expand Up @@ -126,7 +129,9 @@ export class PageEditor extends React.Component<PageEditorProps, PageEditorState
constructor(props: PageEditorProps) {
super(props);

const { title, objectives, allObjectives, content, allTags } = props;
const { title, objectives, allObjectives, content, allTags, defaultEditor } = props;

setDefaultEditor(defaultEditor);

const activityContexts = Immutable.OrderedMap<string, ActivityEditContext>(
this.props.activityContexts.map((c) => {
Expand Down Expand Up @@ -269,7 +274,7 @@ export class PageEditor extends React.Component<PageEditorProps, PageEditorState
// only update if editMode is active
if (!this.state.editMode) return;

const model = this.adjustActivityForConstraints(
const constrainedContent = this.adjustActivityForConstraints(
this.state.activityContexts.get(id)?.typeSlug,
update.content,
);
Expand All @@ -289,7 +294,7 @@ export class PageEditor extends React.Component<PageEditorProps, PageEditorState
this.props.projectSlug,
this.props.resourceId,
merged.activityId,
{ ...update, content: model },
{ ...update, content: constrainedContent },
releaseLock,
);

Expand Down
15 changes: 13 additions & 2 deletions assets/src/components/activities/AuthoringElementProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useContext } from 'react';
import React, { useContext, useRef } from 'react';
import produce from 'immer';
import { Maybe } from 'tsmonad';
import { ErrorBoundary } from 'components/common/ErrorBoundary';
Expand Down Expand Up @@ -33,8 +33,19 @@ export const AuthoringElementProvider: React.FC<AuthoringElementProps<ActivityMo
onRequestMedia,
onEdit,
}) => {
const modelRef = useRef(model);
modelRef.current = model;

const dispatch: AuthoringElementState<any>['dispatch'] = (action) => {
const newModel = produce(model, (draftState) => action(draftState, onPostUndoable));
/*
Got into an interesting situation where this dispatch function was closed over an outdated version of `model`
causing previous updates to be undone.
ie: these were not the same value when called:
model.authoring.parts[0].responses[0].feedback
modelRef.current?.authoring.parts[0].responses[0].feedback
*/
const newModel = produce(modelRef.current, (draftState) => action(draftState, onPostUndoable));
onEdit(newModel);
return newModel;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ const CheckAllThatApply = () => {
addOne={() => dispatch(CATAActions.addChoice(ActivityTypes.makeChoice('')))}
setAll={(choices: ActivityTypes.Choice[]) => dispatch(Choices.setAll(choices))}
onEdit={(id, content) => dispatch(Choices.setContent(id, content))}
onChangeEditorType={(id, editorType) => dispatch(Choices.setEditor(id, editorType))}
onRemove={(id) => dispatch(CATAActions.removeChoiceAndUpdateRules(id))}
/>
</TabbedNavigation.Tab>
Expand Down
100 changes: 0 additions & 100 deletions assets/src/components/activities/common/Hints.tsx

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ import { PREVIEW_TEXT_PATH, STEM_PATH } from 'data/activities/model/utils';
import { Operations } from 'utils/pathOperations';

export const StemActions = {
changeEditorMode(mode: 'slate' | 'markdown', stemPath = STEM_PATH) {
return (model: any) => {
Operations.apply(model, Operations.setKey(stemPath, 'editor', mode));
};
},
editStem(content: Descendant[], stemPath = STEM_PATH) {
return (model: any) => {
Operations.apply(model, Operations.replace(stemPath + '.content', content));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import React from 'react';
import { Descendant } from 'slate';
import { useAuthoringElementContext } from 'components/activities/AuthoringElementProvider';
import { AuthoringButtonConnected } from 'components/activities/common/authoring/AuthoringButton';
import { RemoveButtonConnected } from 'components/activities/common/authoring/RemoveButton';
import { Choice, makeContent } from 'components/activities/types';
import { Draggable } from 'components/common/DraggableColumn';
import { RichTextEditorConnected } from 'components/content/RichTextEditor';
import { SlateOrMarkdownEditor } from 'components/editing/SlateOrMarkdownEditor';
import { toSimpleText } from 'components/editing/slateUtils';
import { DEFAULT_EDITOR, EditorType } from 'data/content/resource';
import { classNames } from 'utils/classNames';
import styles from './ChoicesAuthoring.modules.scss';

Expand All @@ -24,6 +26,7 @@ interface Props {
addOne: () => void;
setAll: (choices: Choice[]) => void;
onEdit: (id: string, content: Descendant[]) => void;
onChangeEditorType?: (id: string, editorType: EditorType) => void;
onRemove: (id: string) => void;
simpleText?: boolean;
colorMap?: Map<string, string>;
Expand All @@ -37,7 +40,10 @@ export const Choices: React.FC<Props> = ({
onRemove,
simpleText,
colorMap,
onChangeEditorType,
}) => {
const { projectSlug } = useAuthoringElementContext();

return (
<>
<Draggable.Column items={choices} setItems={setAll}>
Expand All @@ -61,15 +67,22 @@ export const Choices: React.FC<Props> = ({
onChange={(e) => onEdit(choice.id, makeContent(e.target.value).content)}
/>
) : (
<RichTextEditorConnected
<SlateOrMarkdownEditor
style={{
flexGrow: 1,
cursor: 'text',
backgroundColor: colorMap?.get(choice.id),
}}
editMode={true}
editorType={choice.editor || DEFAULT_EDITOR}
placeholder="Answer choice"
value={choice.content}
content={choice.content}
onEdit={(content) => onEdit(choice.id, content)}
allowBlockElements={true}
onEditorTypeChange={(editor) =>
onChangeEditorType && onChangeEditorType(choice.id, editor)
}
projectSlug={projectSlug}
/>
)}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,32 @@
import React from 'react';
import { useAuthoringElementContext } from 'components/activities/AuthoringElementProvider';
import { HasParts, RichText } from 'components/activities/types';
import { RichTextEditorConnected } from 'components/content/RichTextEditor';
import { getExplanationContent, setExplanationContent } from 'data/activities/model/explanation';
import { SlateOrMarkdownEditor } from 'components/editing/SlateOrMarkdownEditor';
import {
getExplanationContent,
getExplanationEditor,
setExplanationContent,
setExplanationEditor,
} from 'data/activities/model/explanation';
import { EditorType } from 'data/content/resource';

interface Props {
partId: string;
}
export const Explanation: React.FC<Props> = (props) => {
const { dispatch, model } = useAuthoringElementContext<HasParts>();
const { dispatch, model, projectSlug } = useAuthoringElementContext<HasParts>();
return (
<RichTextEditorConnected
<SlateOrMarkdownEditor
placeholder="Explanation"
value={getExplanationContent(model, props.partId)}
content={getExplanationContent(model, props.partId)}
onEdit={(content: RichText) => dispatch(setExplanationContent(props.partId, content))}
onEditorTypeChange={(editor: EditorType) =>
dispatch(setExplanationEditor(props.partId, editor))
}
editMode={true}
editorType={getExplanationEditor(model, props.partId)}
allowBlockElements={true}
projectSlug={projectSlug}
/>
);
};
Loading

0 comments on commit 54316a7

Please sign in to comment.