From 9dd07f53899858d273fac920af0321209ed0fe10 Mon Sep 17 00:00:00 2001
From: Yannis Barlas <yannisbarlas@gmail.com>
Date: Thu, 11 Aug 2022 18:45:27 +0300
Subject: [PATCH] fix(services): add pure readonly state to hhmi services

---
 editors/demo/src/HHMI/HHMI.js                 | 94 +++++++++++++++++--
 editors/demo/src/HHMI/layout/HhmiLayout.js    |  5 +-
 wax-prosemirror-core/src/Wax.js               |  1 +
 wax-prosemirror-services/package.json         |  1 +
 .../components/EditorComponent.js             |  7 +-
 .../FillTheGapContainerComponent.js           |  6 +-
 .../components/DropDownComponent.js           | 10 +-
 .../components/MatchingContainerComponent.js  | 82 +++++++++-------
 .../components/MatchingOptionComponent.js     | 21 +++--
 .../components/AnswerComponent.js             |  3 +-
 .../components/YesNoSwitch.js                 | 16 +++-
 .../MultipleDropDownContainerComponent.js     |  5 +-
 12 files changed, 176 insertions(+), 75 deletions(-)

diff --git a/editors/demo/src/HHMI/HHMI.js b/editors/demo/src/HHMI/HHMI.js
index f161b02fa..cc2adbefa 100644
--- a/editors/demo/src/HHMI/HHMI.js
+++ b/editors/demo/src/HHMI/HHMI.js
@@ -1,5 +1,5 @@
 import React, { useState, useRef } from 'react';
-import styled from 'styled-components';
+import styled, { css } from 'styled-components';
 
 import { Wax } from 'wax-prosemirror-core';
 
@@ -16,16 +16,60 @@ const renderImage = file => {
   });
 };
 
+const NormalButton = styled.button`
+  left: 600px;
+  position: absolute;
+  top: 16px;
+
+  /* stylelint-disable-next-line order/properties-alphabetical-order */
+  ${props =>
+    props.isActive &&
+    css`
+      background-color: gray;
+      color: white;
+    `}
+`;
+
 const ReadOnlyButton = styled.button`
+  left: 670px;
+  position: absolute;
+  top: 16px;
+
+  /* stylelint-disable-next-line order/properties-alphabetical-order */
+  ${props =>
+    props.isActive &&
+    css`
+      background-color: gray;
+      color: white;
+    `}
+`;
+
+const TestModeButton = styled.button`
+  left: 760px;
   position: absolute;
-  left: 600px;
   top: 16px;
+
+  /* stylelint-disable-next-line order/properties-alphabetical-order */
+  ${props =>
+    props.isActive &&
+    css`
+      background-color: gray;
+      color: white;
+    `}
 `;
 
 const SubmitButton = styled.button`
+  left: 850px;
   position: absolute;
-  left: 700px;
   top: 16px;
+
+  /* stylelint-disable-next-line order/properties-alphabetical-order */
+  ${props =>
+    props.isActive &&
+    css`
+      background-color: gray;
+      color: white;
+    `}
 `;
 
 const initialContent = `<p class="paragraph"></p>
@@ -55,32 +99,64 @@ const initialContent = `<p class="paragraph"></p>
 </div>`;
 
 const Hhmi = () => {
-  const [submited, isSubmited] = useState(false);
+  const [submitted, isSubmitted] = useState(false);
   const [readOnly, isReadOnly] = useState(false);
+  const [testMode, isTestMode] = useState(false);
   const [content, setContent] = useState(initialContent);
 
+  const normalQuestions = () => {
+    isReadOnly(false);
+    isTestMode(false);
+    isSubmitted(false);
+    setContent(editorRef.current.getContent());
+  };
+
   const readOnlyQuestions = () => {
+    isReadOnly(true);
+    isTestMode(false);
+    isSubmitted(false);
     setContent(editorRef.current.getContent());
+  };
+
+  const testModeQuestions = () => {
     isReadOnly(true);
+    isTestMode(true);
+    isSubmitted(false);
+    setContent(editorRef.current.getContent());
   };
 
   const submitQuestions = () => {
-    setContent(editorRef.current.getContent());
-    isSubmited(true);
     isReadOnly(true);
+    isTestMode(false);
+    isSubmitted(true);
+    setContent(editorRef.current.getContent());
   };
 
   const editorRef = useRef();
 
   return (
     <>
-      <ReadOnlyButton onClick={readOnlyQuestions}>Read Only</ReadOnlyButton>
-      <SubmitButton onClick={submitQuestions}>Submit</SubmitButton>
+      <NormalButton isActive={!readOnly} onClick={normalQuestions}>
+        Normal
+      </NormalButton>
+      <ReadOnlyButton
+        isActive={readOnly && !submitted && !testMode}
+        onClick={readOnlyQuestions}
+      >
+        Read Only
+      </ReadOnlyButton>
+      <TestModeButton isActive={testMode} onClick={testModeQuestions}>
+        Test Mode
+      </TestModeButton>
+      <SubmitButton isActive={submitted} onClick={submitQuestions}>
+        Submit
+      </SubmitButton>
+
       <Wax
         config={config}
         autoFocus
         ref={editorRef}
-        customValues={{ showFeedBack: submited }}
+        customValues={{ showFeedBack: submitted, testMode }}
         fileUpload={file => renderImage(file)}
         value={content}
         readonly={readOnly}
diff --git a/editors/demo/src/HHMI/layout/HhmiLayout.js b/editors/demo/src/HHMI/layout/HhmiLayout.js
index 436f33949..4179a73e5 100644
--- a/editors/demo/src/HHMI/layout/HhmiLayout.js
+++ b/editors/demo/src/HHMI/layout/HhmiLayout.js
@@ -59,22 +59,23 @@ const EditorArea = styled.div`
 `;
 
 const WaxSurfaceScroll = styled.div`
-  padding: 25px 25% 0 25%;
   box-sizing: border-box;
   display: flex;
   height: 100%;
   overflow-y: auto;
+  padding: 25px 25% 0 25%;
   position: relative;
   width: 100%;
 
   /* PM styles  for main content*/
+  /* stylelint-disable-next-line order/properties-alphabetical-order */
   ${EditorElements};
 `;
 
 const EditorContainer = styled.div`
   height: 100%;
-  width: 100%;
   position: relative;
+  width: 100%;
 
   .ProseMirror {
     box-shadow: 0 0 8px #ecedf1;
diff --git a/wax-prosemirror-core/src/Wax.js b/wax-prosemirror-core/src/Wax.js
index a06ea9def..97b73c65c 100644
--- a/wax-prosemirror-core/src/Wax.js
+++ b/wax-prosemirror-core/src/Wax.js
@@ -96,6 +96,7 @@ const Wax = forwardRef((props, ref) => {
 
 Wax.defaultProps = {
   config: { SchemaService: DefaultSchema, services: [] },
+  customValues: {},
 };
 
 export default Wax;
diff --git a/wax-prosemirror-services/package.json b/wax-prosemirror-services/package.json
index dbdbf9637..ad1a4b739 100644
--- a/wax-prosemirror-services/package.json
+++ b/wax-prosemirror-services/package.json
@@ -27,6 +27,7 @@
     "prosemirror-transform": "1.3.4",
     "prosemirror-view": "1.23.7",
     "rc-switch": "^3.2.2",
+    "react-dropdown": "^1.6.2",
     "styled-components": "^5.3.0",
     "use-deep-compare-effect": "^1.3.1",
     "uuid": "^7.0.3",
diff --git a/wax-prosemirror-services/src/FillTheGapQuestionService/components/EditorComponent.js b/wax-prosemirror-services/src/FillTheGapQuestionService/components/EditorComponent.js
index 957d773ca..40f5efe55 100644
--- a/wax-prosemirror-services/src/FillTheGapQuestionService/components/EditorComponent.js
+++ b/wax-prosemirror-services/src/FillTheGapQuestionService/components/EditorComponent.js
@@ -1,4 +1,5 @@
 /* eslint-disable react/prop-types */
+/* stylelint-disable declaration-no-important */
 
 import React, { useContext, useRef, useEffect } from 'react';
 import styled from 'styled-components';
@@ -147,9 +148,9 @@ const EditorComponent = ({ node, view, getPos }) => {
     if (!tr.getMeta('fromOutside')) {
       const outerTr = view.state.tr;
       const offsetMap = StepMap.offset(getPos() + 1);
-      for (let i = 0; i < transactions.length; i++) {
+      for (let i = 0; i < transactions.length; i += 1) {
         const { steps } = transactions[i];
-        for (let j = 0; j < steps.length; j++)
+        for (let j = 0; j < steps.length; j += 1)
           outerTr.step(steps[j].map(offsetMap));
       }
       if (outerTr.docChanged)
@@ -159,7 +160,7 @@ const EditorComponent = ({ node, view, getPos }) => {
 
   return (
     <>
-      {isEditable ? (
+      {isEditable || (!isEditable && !main.props.customValues.testMode) ? (
         <EditorWrapper>
           <div ref={editorRef} />
         </EditorWrapper>
diff --git a/wax-prosemirror-services/src/FillTheGapQuestionService/components/FillTheGapContainerComponent.js b/wax-prosemirror-services/src/FillTheGapQuestionService/components/FillTheGapContainerComponent.js
index 6a39e42d9..a1b2defc1 100644
--- a/wax-prosemirror-services/src/FillTheGapQuestionService/components/FillTheGapContainerComponent.js
+++ b/wax-prosemirror-services/src/FillTheGapQuestionService/components/FillTheGapContainerComponent.js
@@ -11,9 +11,7 @@ const FillTheGapContainer = styled.div`
 `;
 
 const FillTheGapWrapper = styled.div`
-  margin-bottom: ;
   margin: 0px 38px 15px 38px;
-
   margin-top: 10px;
 `;
 
@@ -24,6 +22,7 @@ export default ({ node, view, getPos }) => {
   } = context;
 
   const customProps = main.props.customValues;
+  const { testMode } = customProps;
 
   const isEditable = main.props.editable(editable => {
     return editable;
@@ -36,7 +35,8 @@ export default ({ node, view, getPos }) => {
       <span>Fill The Gap</span>
       <FillTheGapContainer className="fill-the-gap">
         <ContainerEditor getPos={getPos} node={node} view={view} />
-        {!(readOnly && customProps && !customProps.showFeedBack) && (
+
+        {!testMode && (
           <FeedbackComponent
             getPos={getPos}
             node={node}
diff --git a/wax-prosemirror-services/src/MatchingService/components/DropDownComponent.js b/wax-prosemirror-services/src/MatchingService/components/DropDownComponent.js
index 951a12109..4dfbba335 100644
--- a/wax-prosemirror-services/src/MatchingService/components/DropDownComponent.js
+++ b/wax-prosemirror-services/src/MatchingService/components/DropDownComponent.js
@@ -12,11 +12,12 @@ const Wrapper = styled.div`
   ${ReactDropDownStyles};
 `;
 const DropdownStyled = styled(Dropdown)`
-  display: inline-flex;
   cursor: not-allowed;
+  display: inline-flex;
   margin-left: auto;
   opacity: ${props => (props.select ? 1 : 0.4)};
   pointer-events: ${props => (props.select ? 'default' : 'none')};
+
   .Dropdown-control {
     border: none;
     padding: 8px 30px 8px 10px;
@@ -31,10 +32,11 @@ const DropdownStyled = styled(Dropdown)`
   }
 
   .Dropdown-menu {
-    width: 102%;
+    align-items: flex-start;
     display: flex;
     flex-direction: column;
-    align-items: flex-start;
+    width: 102%;
+
     .Dropdown-option {
       width: 100%;
     }
@@ -85,7 +87,7 @@ const DropComponent = ({ getPos, node, view }) => {
           placeholder="Select option"
           select
           value={
-            selectedOption === 'undedfined' ? 'Select Option' : selectedOption
+            selectedOption === 'undefined' ? 'Select Option' : selectedOption
           }
         />
       </Wrapper>
diff --git a/wax-prosemirror-services/src/MatchingService/components/MatchingContainerComponent.js b/wax-prosemirror-services/src/MatchingService/components/MatchingContainerComponent.js
index 31b7dcd63..166d1a35d 100644
--- a/wax-prosemirror-services/src/MatchingService/components/MatchingContainerComponent.js
+++ b/wax-prosemirror-services/src/MatchingService/components/MatchingContainerComponent.js
@@ -29,10 +29,10 @@ const QuestionWrapper = styled.div`
 `;
 
 const ActionButton = styled.button`
-  height: 24px;
-  border: none;
   background: transparent;
+  border: none;
   cursor: pointer;
+  height: 24px;
   padding-left: 0;
 `;
 
@@ -53,27 +53,27 @@ const OptionArea = styled.div`
 
   ul {
     display: flex;
-    flex-wrap: wrap;
     flex-direction: row;
+    flex-wrap: wrap;
     margin: 0;
     padding: 0;
+
     li {
       list-style-type: none;
-      padding-right: 7px;
       padding-bottom: 7px;
+      padding-right: 7px;
 
       span {
         background: #535e76;
+        border-radius: 12px;
         color: white;
         padding: 3px 3px 3px 10px;
-        border-radius: 12px;
-      }
-      buttton {
       }
+
       svg {
         fill: white;
-        width: 16px;
         height: 16px;
+        width: 16px;
       }
     }
   }
@@ -81,9 +81,11 @@ const OptionArea = styled.div`
 
 const AddOption = styled.div`
   display: flex;
+
   input {
     border: none;
     border-bottom: 1px solid black;
+
     &:focus {
       outline: none;
     }
@@ -93,21 +95,21 @@ const AddOption = styled.div`
       font-style: italic;
     }
   }
+
   button {
+    background: #fff;
     border: 1px solid #535e76;
-    cursor: pointer;
     color: #535e76;
+    cursor: pointer;
     margin-left: 20px;
-    background: #fff;
     padding: 4px 8px 4px 8px;
+
     &:hover {
+      background: #535e76;
       border: 1px solid #535e76;
+      color: #fff;
       cursor: pointer;
-      color: #535e76;
       margin-right: 20px;
-      background: #fff;
-      background: #535e76;
-      color: #fff;
       padding: 4px 8px 4px 8px;
     }
   }
@@ -136,7 +138,7 @@ export default ({ node, view, getPos }) => {
   useEffect(() => {
     const allNodes = getNodes(main);
 
-    /*TEMP TO SAVE NODE OPTIONS TODO: SAVE IN CONTEXT OPTIONS*/
+    /* TEMP TO SAVE NODE OPTIONS TODO: SAVE IN CONTEXT OPTIONS */
     saveInChildOptions(allNodes);
 
     if (!addingOption) return;
@@ -190,6 +192,7 @@ export default ({ node, view, getPos }) => {
         singleNode.node.content.content.forEach(parentNodes => {
           parentNodes.forEach(optionNode => {
             if (optionNode.type.name === 'matching_option')
+              /* eslint-disable-next-line no-param-reassign */
               optionNode.attrs.options = options;
           });
         });
@@ -204,7 +207,8 @@ export default ({ node, view, getPos }) => {
         <QuestionWrapper>
           <ContainerEditor getPos={getPos} node={node} view={view} />
         </QuestionWrapper>
-        {!readOnly && (
+        {(!readOnly ||
+          (readOnly && !customProps.testMode && !customProps.showFeedBack)) && (
           <CreateOptions>
             <OptionArea>
               {options.length > 0 && (
@@ -215,12 +219,14 @@ export default ({ node, view, getPos }) => {
                       <li key={option.value}>
                         <span>
                           {option.label} &nbsp;
-                          <ActionButton
-                            onClick={() => removeOption(option.value)}
-                            type="button"
-                          >
-                            <StyledIconAction name="deleteOutlined" />
-                          </ActionButton>
+                          {!readOnly && (
+                            <ActionButton
+                              onClick={() => removeOption(option.value)}
+                              type="button"
+                            >
+                              <StyledIconAction name="deleteOutlined" />
+                            </ActionButton>
+                          )}
                         </span>
                       </li>
                     );
@@ -228,22 +234,26 @@ export default ({ node, view, getPos }) => {
                 </ul>
               )}
             </OptionArea>
-            <AddOption>
-              <input
-                onChange={updateOptionText}
-                onKeyPress={handleKeyDown}
-                placeholder="Type an option ..."
-                ref={addOptionRef}
-                type="text"
-                value={optionText}
-              />
-              <button onClick={addOption} type="button">
-                Add Option
-              </button>
-            </AddOption>
+
+            {!readOnly && (
+              <AddOption>
+                <input
+                  onChange={updateOptionText}
+                  onKeyPress={handleKeyDown}
+                  placeholder="Type an option ..."
+                  ref={addOptionRef}
+                  type="text"
+                  value={optionText}
+                />
+                <button onClick={addOption} type="button">
+                  Add Option
+                </button>
+              </AddOption>
+            )}
           </CreateOptions>
         )}
-        {!(readOnly && !customProps.showFeedBack) && (
+        {(!(readOnly && !customProps.showFeedBack) ||
+          (readOnly && !customProps.testMode && !customProps.showFeedBack)) && (
           <FeedbackComponent
             getPos={getPos}
             node={node}
diff --git a/wax-prosemirror-services/src/MatchingService/components/MatchingOptionComponent.js b/wax-prosemirror-services/src/MatchingService/components/MatchingOptionComponent.js
index 71a30cccf..5bd695e1b 100644
--- a/wax-prosemirror-services/src/MatchingService/components/MatchingOptionComponent.js
+++ b/wax-prosemirror-services/src/MatchingService/components/MatchingOptionComponent.js
@@ -1,5 +1,5 @@
 /* eslint-disable react/prop-types */
-import React, { useContext, useState } from 'react';
+import React, { useContext } from 'react';
 import { v4 as uuidv4 } from 'uuid';
 import { TextSelection } from 'prosemirror-state';
 import { Fragment } from 'prosemirror-model';
@@ -13,28 +13,28 @@ import ReadOnlyDropDownComponent from './ReadOnlyDropDownComponent';
 const Option = styled.div`
   display: flex;
   flex-direction: row;
-  width: 100%;
   padding-bottom: 10px;
+  width: 100%;
 `;
 
 const ButtonsContainer = styled.div`
-  width: 7%;
   display: flex;
   flex-direction: column;
   justify-content: center;
+  width: 7%;
 `;
 
 const DropDownContainer = styled.div`
   display: flex;
-  justify-content: center;
   flex-direction: column;
+  justify-content: center;
 `;
 
 const ActionButton = styled.button`
-  height: 24px;
-  border: none;
   background: transparent;
+  border: none;
   cursor: pointer;
+  height: 24px;
   padding-left: 0;
 `;
 
@@ -44,9 +44,9 @@ const StyledIconAction = styled(Icon)`
 `;
 
 const AnswerContainer = styled.div`
-  margin-left: 10px;
   display: flex;
   flex-direction: column;
+  margin-left: 10px;
 `;
 const CorrectAnswer = styled.span`
   span {
@@ -72,6 +72,7 @@ export default ({ node, view, getPos }) => {
 
   const readOnly = !isEditable;
   const customProps = main.props.customValues;
+  const { testMode, showFeedBack } = customProps;
 
   const addAnswer = () => {
     const nodeId = node.attrs.id;
@@ -133,15 +134,15 @@ export default ({ node, view, getPos }) => {
       )}
       <EditorComponent getPos={getPos} node={node} view={view} />
       <DropDownContainer>
-        {!readOnly && (
+        {(!readOnly || (readOnly && !testMode && !showFeedBack)) && (
           <DropDownComponent getPos={getPos} node={node} view={view} />
         )}
 
-        {readOnly && customProps && !customProps.showFeedBack && (
+        {readOnly && testMode && !showFeedBack && (
           <ReadOnlyDropDownComponent getPos={getPos} node={node} view={view} />
         )}
 
-        {readOnly && customProps && customProps.showFeedBack && (
+        {readOnly && showFeedBack && (
           <AnswerContainer>
             <CorrectAnswer>
               Correct : &nbsp;{correct && <span>{correct.label} </span>}
diff --git a/wax-prosemirror-services/src/MultipleChoiceQuestionService/components/AnswerComponent.js b/wax-prosemirror-services/src/MultipleChoiceQuestionService/components/AnswerComponent.js
index c4b4574da..d4a73181b 100644
--- a/wax-prosemirror-services/src/MultipleChoiceQuestionService/components/AnswerComponent.js
+++ b/wax-prosemirror-services/src/MultipleChoiceQuestionService/components/AnswerComponent.js
@@ -172,6 +172,7 @@ export default ({ node, view, getPos }) => {
   };
 
   const readOnly = !isEditable;
+  const { testMode } = customProps;
 
   return (
     <Wrapper>
@@ -184,7 +185,7 @@ export default ({ node, view, getPos }) => {
           <QuestionData>
             <EditorComponent getPos={getPos} node={node} view={view} />
           </QuestionData>
-          {!(readOnly && customProps && !customProps.showFeedBack) && (
+          {!testMode && (
             <FeedbackComponent
               getPos={getPos}
               node={node}
diff --git a/wax-prosemirror-services/src/MultipleChoiceQuestionService/components/YesNoSwitch.js b/wax-prosemirror-services/src/MultipleChoiceQuestionService/components/YesNoSwitch.js
index b68f967f5..0b12a6a49 100644
--- a/wax-prosemirror-services/src/MultipleChoiceQuestionService/components/YesNoSwitch.js
+++ b/wax-prosemirror-services/src/MultipleChoiceQuestionService/components/YesNoSwitch.js
@@ -15,6 +15,7 @@ const AnswerContainer = styled.span`
 
 const Correct = styled.span`
   margin-right: 10px;
+
   span {
     color: #008000;
   }
@@ -22,6 +23,7 @@ const Correct = styled.span`
 
 const Answer = styled.span`
   margin-right: 10px;
+
   span {
     color: ${props => (props.isCorrect ? ' #008000' : 'red')};
   }
@@ -29,15 +31,15 @@ const Answer = styled.span`
 
 const StyledIconCorrect = styled(Icon)`
   fill: #008000;
-  pointer-events: none;
   height: 24px;
+  pointer-events: none;
   width: 24px;
 `;
 
 const StyledIconWrong = styled(Icon)`
   fill: red;
-  pointer-events: none;
   height: 24px;
+  pointer-events: none;
   width: 24px;
 `;
 const YesNoSwitch = ({
@@ -48,7 +50,9 @@ const YesNoSwitch = ({
   checked,
   checkedAnswerMode,
 }) => {
-  if (customProps && customProps.showFeedBack) {
+  const { testMode, showFeedBack } = customProps;
+
+  if (showFeedBack) {
     const correct = node.attrs.correct ? 'YES' : 'NO';
     const answer = node.attrs.answer ? 'YES' : 'NO';
     const isCorrect = node.attrs.correct === node.attrs.answer;
@@ -68,10 +72,14 @@ const YesNoSwitch = ({
       </AnswerContainer>
     );
   }
+
   return (
     <StyledSwitch
-      checked={isEditable ? checked : checkedAnswerMode}
+      checked={
+        isEditable || (!isEditable && !testMode) ? checked : checkedAnswerMode
+      }
       checkedChildren="YES"
+      disabled={!isEditable && !testMode}
       label="Correct?"
       labelPosition="left"
       onChange={handleChange}
diff --git a/wax-prosemirror-services/src/MultipleDropDownService/components/MultipleDropDownContainerComponent.js b/wax-prosemirror-services/src/MultipleDropDownService/components/MultipleDropDownContainerComponent.js
index d6725bb7d..acc86907c 100644
--- a/wax-prosemirror-services/src/MultipleDropDownService/components/MultipleDropDownContainerComponent.js
+++ b/wax-prosemirror-services/src/MultipleDropDownService/components/MultipleDropDownContainerComponent.js
@@ -6,9 +6,7 @@ import ContainerEditor from './ContainerEditor';
 import FeedbackComponent from './FeedbackComponent';
 
 const MultipleDropDownpWrapper = styled.div`
-  margin-bottom: ;
   margin: 0px 38px 15px 38px;
-
   margin-top: 10px;
 `;
 
@@ -30,13 +28,14 @@ export default ({ node, view, getPos }) => {
   });
 
   const readOnly = !isEditable;
+  const { testMode } = customProps;
 
   return (
     <MultipleDropDownpWrapper>
       <span>Multiple Drop Down</span>
       <MultipleDropDownpContainer className="multiple-drop-down">
         <ContainerEditor getPos={getPos} node={node} view={view} />
-        {!(readOnly && customProps && !customProps.showFeedBack) && (
+        {!testMode && (
           <FeedbackComponent
             getPos={getPos}
             node={node}
-- 
GitLab