@elice/material-quiz 1.230203.0 → 1.230208.0-style.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/cjs/components/material-quiz/MaterialQuiz.i18n.d.ts +4 -1
  2. package/cjs/components/material-quiz/MaterialQuiz.i18n.js +9 -3
  3. package/cjs/components/material-quiz/MaterialQuiz.js +2 -0
  4. package/cjs/components/material-quiz/MaterialQuizInfo.js +17 -23
  5. package/cjs/components/material-quiz/MaterialQuizSelectMultiple.js +22 -15
  6. package/cjs/components/material-quiz/MaterialQuizSelectMultipleOrder.js +8 -4
  7. package/cjs/components/material-quiz/MaterialQuizSelectOne.js +20 -13
  8. package/cjs/components/material-quiz/MaterialQuizShimmer.js +3 -6
  9. package/cjs/components/material-quiz/MaterialQuizText.js +9 -2
  10. package/cjs/components/material-quiz/context/MaterialQuizContext.d.ts +3 -1
  11. package/cjs/components/material-quiz/context/MaterialQuizContext.js +2 -0
  12. package/cjs/components/material-quiz/material-quiz-group/MaterialQuizGroup.js +6 -1
  13. package/cjs/components/material-quiz/material-quiz-group/context/context.d.ts +1 -0
  14. package/cjs/components/shared/QuestionBox.d.ts +1 -0
  15. package/cjs/components/shared/QuestionBox.js +16 -8
  16. package/cjs/components/shared/QuizDraggbleOption.js +1 -1
  17. package/cjs/components/shared/question-checkbox/QuestionCheckboxOption.js +1 -1
  18. package/cjs/components/shared/question-radio/QuestionRadioOption.js +1 -1
  19. package/es/components/material-quiz/MaterialQuiz.i18n.d.ts +4 -1
  20. package/es/components/material-quiz/MaterialQuiz.i18n.js +9 -3
  21. package/es/components/material-quiz/MaterialQuiz.js +2 -0
  22. package/es/components/material-quiz/MaterialQuizInfo.js +17 -24
  23. package/es/components/material-quiz/MaterialQuizSelectMultiple.js +22 -15
  24. package/es/components/material-quiz/MaterialQuizSelectMultipleOrder.js +8 -4
  25. package/es/components/material-quiz/MaterialQuizSelectOne.js +20 -13
  26. package/es/components/material-quiz/MaterialQuizShimmer.js +3 -6
  27. package/es/components/material-quiz/MaterialQuizText.js +9 -2
  28. package/es/components/material-quiz/context/MaterialQuizContext.d.ts +3 -1
  29. package/es/components/material-quiz/context/MaterialQuizContext.js +2 -0
  30. package/es/components/material-quiz/material-quiz-group/MaterialQuizGroup.js +6 -1
  31. package/es/components/material-quiz/material-quiz-group/context/context.d.ts +1 -0
  32. package/es/components/shared/QuestionBox.d.ts +1 -0
  33. package/es/components/shared/QuestionBox.js +17 -9
  34. package/es/components/shared/QuizDraggbleOption.js +1 -1
  35. package/es/components/shared/question-checkbox/QuestionCheckboxOption.js +1 -1
  36. package/es/components/shared/question-radio/QuestionRadioOption.js +1 -1
  37. package/package.json +4 -4
@@ -4,7 +4,10 @@ export declare const en: {
4
4
  'materialQuiz.submittedAnswer': string;
5
5
  'materialQuiz.answer': string;
6
6
  'materialQuiz.next': string;
7
- 'materialQuiz.myAnswer': string;
7
+ 'materialQuiz.selectOne.answer.title': string;
8
+ 'materialQuiz.selectMultiple.answer.title': string;
9
+ 'materialQuiz.selectMultipleOrder.answer.title': string;
10
+ 'materialQuiz.text.answer.title': string;
8
11
  'materialQuiz.empty.title': string;
9
12
  'materialQuiz.empty.description': string;
10
13
  'materialQuiz.survey.empty.title': string;
@@ -8,7 +8,10 @@ const en = {
8
8
  'materialQuiz.submittedAnswer': 'Submitted Answer',
9
9
  'materialQuiz.answer': 'Answer',
10
10
  'materialQuiz.next': 'Next question >',
11
- 'materialQuiz.myAnswer': 'My answer',
11
+ 'materialQuiz.selectOne.answer.title': 'Please choose the right answer.',
12
+ 'materialQuiz.selectMultiple.answer.title': 'Please choose the right answer. (Redundant selections available)',
13
+ 'materialQuiz.selectMultipleOrder.answer.title': 'Drag and drop your answers.',
14
+ 'materialQuiz.text.answer.title': 'Please fill out the answer.',
12
15
  'materialQuiz.empty.title': 'Quiz is now preparing.',
13
16
  'materialQuiz.empty.description': 'Please check again after quiz posting!',
14
17
  'materialQuiz.survey.empty.title': 'Survey is now preparing.',
@@ -38,7 +41,10 @@ const ko = {
38
41
  'materialQuiz.submittedAnswer': '제출한 답',
39
42
  'materialQuiz.answer': '정답',
40
43
  'materialQuiz.next': '다음 문제로 이동 >',
41
- 'materialQuiz.myAnswer': ' 답안',
44
+ 'materialQuiz.selectOne.answer.title': '알맞는 답을 선택해 주세요.',
45
+ 'materialQuiz.selectMultiple.answer.title': '알맞는 답을 선택해 주세요. (중복 선택 가능)',
46
+ 'materialQuiz.selectMultipleOrder.answer.title': '답안을 드래그 앤 드롭하세요.',
47
+ 'materialQuiz.text.answer.title': '답안을 작성해 주세요.',
42
48
  'materialQuiz.empty.title': '퀴즈가 준비 중 입니다.',
43
49
  'materialQuiz.empty.description': '퀴즈 게시 후 다시 확인해주세요!',
44
50
  'materialQuiz.survey.empty.title': '설문조사 준비 중 입니다.',
@@ -51,7 +57,7 @@ const ko = {
51
57
  'materialQuiz.explanation.close': '퀴즈해설 닫기',
52
58
  'materialQuiz.explanation.show': '퀴즈 해설 보기',
53
59
  'materialQuiz.explanation.empty': '작성된 해설이 없습니다.',
54
- 'materialQuiz.text.placeholder': '답을 입력하세요.',
60
+ 'materialQuiz.text.placeholder': '답안을 작성해 주세요.',
55
61
  'materialQuiz.text.correct': '정답입니다.',
56
62
  'materialQuiz.text.wrong': '오답입니다. 다시 답안을 제출해보세요.',
57
63
  'materialQuiz.order.answerEmpty': '항목을 다시 이곳으로 옮길 수 있습니다.',
@@ -102,6 +102,7 @@ const MaterialQuizContainer = ({
102
102
  materialQuizId,
103
103
  userId,
104
104
  locale = 'en',
105
+ onDirty,
105
106
  onSubmit,
106
107
  onNext
107
108
  }) => {
@@ -112,6 +113,7 @@ const MaterialQuizContainer = ({
112
113
  }, React__default["default"].createElement(MaterialQuizContext.MaterialQuizProvider, {
113
114
  materialQuizId: materialQuizId,
114
115
  userId: userId,
116
+ onDirty: onDirty,
115
117
  onSubmit: onSubmit,
116
118
  onNext: onNext
117
119
  }, React__default["default"].createElement(MaterialQuiz, null)));
@@ -2,8 +2,8 @@
2
2
 
3
3
  var React = require('react');
4
4
  var blocks = require('@elice/blocks');
5
- var designTokens = require('@elice/design-tokens');
6
5
  var markdown = require('@elice/markdown');
6
+ var styled = require('styled-components');
7
7
  var QuestionBox = require('../shared/QuestionBox.js');
8
8
  var MaterialQuizContext = require('./context/MaterialQuizContext.js');
9
9
  var MaterialQuizAnswerExplanation = require('./MaterialQuizAnswerExplanation.js');
@@ -11,38 +11,32 @@ var MaterialQuizAnswerExplanation = require('./MaterialQuizAnswerExplanation.js'
11
11
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
12
12
 
13
13
  var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
14
+ var styled__default = /*#__PURE__*/_interopDefaultLegacy(styled);
15
+
16
+ //
17
+ //
18
+
19
+ const StyledMarkdownSSR = styled__default["default"](markdown.MarkdownSSR).withConfig({
20
+ componentId: "sc-1s7jbf9-0"
21
+ })([".elicemd--theme-dark{pre{border:0;}table td,table th{border-bottom-color:rgba(255,255,255,0.16);}}"]); //
22
+ //
23
+ //
14
24
 
15
25
  const MaterialQuizInfo = () => {
16
26
  const {
17
27
  materialQuiz
18
28
  } = MaterialQuizContext.useMaterialQuizState();
19
29
  return React__default["default"].createElement(QuestionBox["default"], {
20
- title: materialQuiz === null || materialQuiz === void 0 ? void 0 : materialQuiz.title
21
- }, materialQuiz ? React__default["default"].createElement(blocks.Flex, null, React__default["default"].createElement(blocks.Text, {
22
- bold: true,
23
- size: "large",
24
- customStyles: {
25
- color: designTokens.base.color.primary3,
26
- marginRight: '0.5rem'
27
- }
28
- }, 'Q.'), React__default["default"].createElement(blocks.Text, {
29
- bold: true,
30
- role: "white",
31
- size: "large",
32
- wordBreak: "break-word"
33
- }, materialQuiz.questionTitle)) : React__default["default"].createElement(blocks.Shimmer, {
34
- dark: true,
35
- borderRadius: '4px',
36
- width: "80%",
37
- height: '40px'
38
- }), React__default["default"].createElement(blocks.Vspace, {
39
- height: 1
40
- }), materialQuiz ? React__default["default"].createElement(markdown.MarkdownSSR, {
30
+ title: materialQuiz === null || materialQuiz === void 0 ? void 0 : materialQuiz.questionTitle,
31
+ titlePrefix: "Q."
32
+ }, materialQuiz ? React__default["default"].createElement(StyledMarkdownSSR, {
41
33
  children: materialQuiz.questionDescription,
42
34
  dark: true,
43
35
  paddingx: 0,
44
36
  paddingy: 0
45
- }) : React__default["default"].createElement(React__default["default"].Fragment, null, React__default["default"].createElement(blocks.Shimmer, {
37
+ }) : React__default["default"].createElement(React__default["default"].Fragment, null, React__default["default"].createElement(blocks.Vspace, {
38
+ height: 1
39
+ }), React__default["default"].createElement(blocks.Shimmer, {
46
40
  dark: true,
47
41
  borderRadius: '4px',
48
42
  width: "80%",
@@ -31,7 +31,8 @@ const MaterialQuizSelectMultiple = () => {
31
31
  const {
32
32
  onSubmit,
33
33
  onNext,
34
- refreshOrgMaterialQuiz
34
+ refreshOrgMaterialQuiz,
35
+ onDirty
35
36
  } = MaterialQuizContext.useMaterialQuizDispatch(); // state
36
37
 
37
38
  const intl = reactIntl.useIntl();
@@ -40,7 +41,22 @@ const MaterialQuizSelectMultiple = () => {
40
41
  const [submitStatus, setSubmitStatus] = React__default["default"].useState('idle'); // Whether user has clicked any options
41
42
 
42
43
  const [isActive, setIsActive] = React__default["default"].useState(false);
43
- const [hasSubmitted, setHasSubmitted] = React__default["default"].useState(false); // quiz response fetcher
44
+ const [hasSubmitted, setHasSubmitted] = React__default["default"].useState(false); // answer select handler
45
+
46
+ const handleAnswerSelect = index$1 => {
47
+ if (index.checkUserLectureTestEnded(lecture) || !!userId) {
48
+ return;
49
+ }
50
+
51
+ onDirty(true);
52
+ setHasSubmitted(false);
53
+ setIsActive(true);
54
+ setSelectedAnswer(prevState => {
55
+ const newState = prevState.includes(index$1) ? prevState.filter(ident => ident !== index$1) : [...prevState, index$1];
56
+ return newState;
57
+ });
58
+ }; // quiz response fetcher
59
+
44
60
 
45
61
  React__default["default"].useEffect(() => {
46
62
  if (!userId && (materialQuiz === null || materialQuiz === void 0 ? void 0 : materialQuiz.lastQuizResponseId)) {
@@ -125,6 +141,7 @@ const MaterialQuizSelectMultiple = () => {
125
141
  onSubmit(true, index.getQuizResult(quizResponse));
126
142
  setSubmitStatus('resolved');
127
143
  setHasSubmitted(true);
144
+ onDirty(false);
128
145
  } catch (error) {
129
146
  console.error(error);
130
147
  onSubmit(false);
@@ -134,8 +151,9 @@ const MaterialQuizSelectMultiple = () => {
134
151
 
135
152
  return React__default["default"].createElement(QuestionBox["default"], {
136
153
  title: intl.formatMessage({
137
- id: 'materialQuiz.myAnswer'
154
+ id: 'materialQuiz.selectMultiple.answer.title'
138
155
  }),
156
+ titlePrefix: "A.",
139
157
  onNext: onNext,
140
158
  isNextActive: hasSubmitted && typeof onNext === 'function',
141
159
  submitResult: React__default["default"].createElement(QuizResultBadge, {
@@ -157,18 +175,7 @@ const MaterialQuizSelectMultiple = () => {
157
175
  })
158
176
  }]
159
177
  }, React__default["default"].createElement(QuestionCheckbox, {
160
- onSelect: index$1 => {
161
- if (index.checkUserLectureTestEnded(lecture) || !!userId) {
162
- return;
163
- }
164
-
165
- setHasSubmitted(false);
166
- setIsActive(true);
167
- setSelectedAnswer(prevState => {
168
- const newState = prevState.includes(index$1) ? prevState.filter(ident => ident !== index$1) : [...prevState, index$1];
169
- return newState;
170
- });
171
- },
178
+ onSelect: handleAnswerSelect,
172
179
  selectedOptions: selectedAnswer,
173
180
  disabled: submitStatus === 'pending' || index.checkUserLectureTestEnded(lecture) || !!userId
174
181
  }, (materialQuiz === null || materialQuiz === void 0 ? void 0 : materialQuiz.options) ? materialQuiz.options.map((option, index$1) => {
@@ -45,7 +45,7 @@ const StyledHorizontalDivider = styled__default["default"].hr.withConfig({
45
45
  });
46
46
  const StyledQuizOptionsDropZone = styled__default["default"](EbDroppable).withConfig({
47
47
  componentId: "sc-10067nt-2"
48
- })(["display:flex;flex-direction:column;flex:1 1 50%;padding:1rem;border-radius:8px;background-color:", ";& > div{margin-bottom:1rem;}&.ui-droppable-hover{outline:1px dashed ", ";}"], designTokens.base.color.navy6, designTokens.base.color.gray4);
48
+ })(["display:flex;flex-direction:column;flex:1 1 50%;padding:1rem;border-radius:8px;background-color:", ";& > div{margin-bottom:1rem;}&.ui-droppable-hover{outline:1px dashed ", ";}"], designTokens.base.color.navy7, designTokens.base.color.gray4);
49
49
  const StyledQuizAnswersDropZone = styled__default["default"].div.withConfig({
50
50
  componentId: "sc-10067nt-3"
51
51
  })(["position:relative;display:flex;flex-direction:column;flex:1 1 50%;padding:1rem;border-radius:8px;background-color:", ";& > div{margin-bottom:1rem;}"], designTokens.base.color.navy6);
@@ -71,7 +71,8 @@ const MaterialQuizSelectMultipleOrder = () => {
71
71
  const {
72
72
  onSubmit,
73
73
  onNext,
74
- refreshOrgMaterialQuiz
74
+ refreshOrgMaterialQuiz,
75
+ onDirty
75
76
  } = MaterialQuizContext.useMaterialQuizDispatch(); // state
76
77
 
77
78
  const intl = reactIntl.useIntl();
@@ -286,6 +287,7 @@ const MaterialQuizSelectMultipleOrder = () => {
286
287
  void refreshOrgMaterialQuiz();
287
288
  onSubmit(true, index.getQuizResult(quizResponse));
288
289
  setSubmitStatus('resolved');
290
+ onDirty(false);
289
291
  } catch (error) {
290
292
  console.error(error);
291
293
  onSubmit(false);
@@ -377,7 +379,8 @@ const MaterialQuizSelectMultipleOrder = () => {
377
379
  }
378
380
 
379
381
  setIsActive(true);
380
- setHasSubmitted(false); // from answerList to answerList
382
+ setHasSubmitted(false);
383
+ onDirty(true); // from answerList to answerList
381
384
 
382
385
  if (ui.draggable[0].classList.contains('quiz-answer')) {
383
386
  const targetOptionId = Number(ui.draggable[0].id);
@@ -429,8 +432,9 @@ const MaterialQuizSelectMultipleOrder = () => {
429
432
 
430
433
  return React__default["default"].createElement(QuestionBox["default"], {
431
434
  title: intl.formatMessage({
432
- id: 'materialQuiz.myAnswer'
435
+ id: 'materialQuiz.selectMultipleOrder.answer.title'
433
436
  }),
437
+ titlePrefix: "A.",
434
438
  onNext: onNext,
435
439
  isNextActive: hasSubmitted && typeof onNext === 'function',
436
440
  submitStatus: React__default["default"].createElement(QuizSubmitStatusText, {
@@ -31,7 +31,8 @@ const MaterialQuizSelectOne = () => {
31
31
  const {
32
32
  onSubmit,
33
33
  onNext,
34
- refreshOrgMaterialQuiz
34
+ refreshOrgMaterialQuiz,
35
+ onDirty
35
36
  } = MaterialQuizContext.useMaterialQuizDispatch(); // state
36
37
 
37
38
  const intl = reactIntl.useIntl();
@@ -40,7 +41,19 @@ const MaterialQuizSelectOne = () => {
40
41
  const [submitStatus, setSubmitStatus] = React__default["default"].useState('idle'); // Whether user has clicked any options
41
42
 
42
43
  const [isActive, setIsActive] = React__default["default"].useState(false);
43
- const [hasSubmitted, setHasSubmitted] = React__default["default"].useState(false); // quiz response fetcher
44
+ const [hasSubmitted, setHasSubmitted] = React__default["default"].useState(false); // answer select handler
45
+
46
+ const handleAnswerSelect = index$1 => {
47
+ if (index.checkUserLectureTestEnded(lecture) || !!userId) {
48
+ return;
49
+ }
50
+
51
+ setHasSubmitted(false);
52
+ setIsActive(true);
53
+ setSelectedAnswer(index$1);
54
+ onDirty(true);
55
+ }; // quiz response fetcher
56
+
44
57
 
45
58
  React__default["default"].useEffect(() => {
46
59
  if (!userId && (materialQuiz === null || materialQuiz === void 0 ? void 0 : materialQuiz.lastQuizResponseId)) {
@@ -115,6 +128,7 @@ const MaterialQuizSelectOne = () => {
115
128
  onSubmit(true, index.getQuizResult(quizResponse));
116
129
  setSubmitStatus('resolved');
117
130
  setHasSubmitted(true);
131
+ onDirty(false);
118
132
  } catch (error) {
119
133
  console.error(error);
120
134
  onSubmit(false);
@@ -124,8 +138,9 @@ const MaterialQuizSelectOne = () => {
124
138
 
125
139
  return React__default["default"].createElement(QuestionBox["default"], {
126
140
  title: intl.formatMessage({
127
- id: 'materialQuiz.myAnswer'
141
+ id: 'materialQuiz.selectOne.answer.title'
128
142
  }),
143
+ titlePrefix: "A.",
129
144
  onNext: onNext,
130
145
  isNextActive: hasSubmitted && typeof onNext === 'function',
131
146
  submitStatus: React__default["default"].createElement(QuizSubmitStatusText, {
@@ -148,15 +163,7 @@ const MaterialQuizSelectOne = () => {
148
163
  }]
149
164
  }, materialQuiz ? React__default["default"].createElement(QuestionRadio, {
150
165
  selectedValue: selectedAnswer,
151
- onSelect: index$1 => {
152
- if (index.checkUserLectureTestEnded(lecture) || !!userId) {
153
- return;
154
- }
155
-
156
- setHasSubmitted(false);
157
- setIsActive(true);
158
- setSelectedAnswer(index$1);
159
- },
166
+ onSelect: handleAnswerSelect,
160
167
  disabled: submitStatus === 'pending' || index.checkUserLectureTestEnded(lecture) || !!userId
161
168
  }, materialQuiz.options ? materialQuiz.options.map((option, index$1) => {
162
169
  var _a, _b;
@@ -169,7 +176,7 @@ const MaterialQuizSelectOne = () => {
169
176
  }
170
177
 
171
178
  const materialQuizResponseAnswer = materialQuizResponse.answer;
172
- return (_a = materialQuizResponseAnswer.includes(index)) !== null && _a !== void 0 ? _a : false;
179
+ return (_a = materialQuizResponseAnswer === null || materialQuizResponseAnswer === void 0 ? void 0 : materialQuizResponseAnswer.includes(index)) !== null && _a !== void 0 ? _a : false;
173
180
  };
174
181
 
175
182
  const status = index.getOptionStatus({
@@ -16,12 +16,9 @@ const StyledShimmer = styled__default["default"].div.withConfig({
16
16
  })(["display:flex;flex-direction:column;border-radius:", ";overflow:hidden;width:100%;"], ({
17
17
  vertical
18
18
  }) => vertical ? '0' : '8px');
19
- const StyledShimmerHeader = styled__default["default"].div.withConfig({
20
- componentId: "sc-10uz5tw-1"
21
- })(["display:flex;justify-content:space-between;background-color:", ";padding:1rem 1.5rem;height:3.5rem;"], designTokens.base.color.navy6);
22
19
  const StyledShimmerBody = styled__default["default"].div.withConfig({
23
- componentId: "sc-10uz5tw-2"
24
- })(["padding:1.5rem;background-color:", ";overflow-y:auto;height:100%;"], designTokens.base.color.navy7);
20
+ componentId: "sc-10uz5tw-1"
21
+ })(["padding:1.5rem;background-color:", ";overflow-y:auto;height:100%;"], designTokens.base.color.navy8);
25
22
 
26
23
  const MaterialQuizShimmer = () => {
27
24
  const {
@@ -29,7 +26,7 @@ const MaterialQuizShimmer = () => {
29
26
  } = MaterialQuizContext.useMaterialQuizState();
30
27
  return React__default["default"].createElement(StyledShimmer, {
31
28
  vertical: vertical
32
- }, React__default["default"].createElement(StyledShimmerHeader, null), React__default["default"].createElement(StyledShimmerBody, null, React__default["default"].createElement(blocks.Shimmer, {
29
+ }, React__default["default"].createElement(StyledShimmerBody, null, React__default["default"].createElement(blocks.Shimmer, {
33
30
  dark: true,
34
31
  borderRadius: '4px',
35
32
  width: "80%",
@@ -19,7 +19,7 @@ var styled__default = /*#__PURE__*/_interopDefaultLegacy(styled);
19
19
 
20
20
  const StyledTextarea = styled__default["default"].textarea.withConfig({
21
21
  componentId: "sc-186cnpj-0"
22
- })(["min-height:12rem;width:100%;padding:1rem 1.375rem;font-size:1rem;color:", ";border-radius:4px;resize:vertical;line-height:1.5rem;outline:none;background-color:", ";&::placeholder{color:", ";}&:hover{cursor:", ";}"], designTokens.base.color.navy2, designTokens.base.color.navy5, designTokens.base.color.navy2, ({
22
+ })(["min-height:12rem;width:100%;padding:1rem 1.375rem;font-size:1rem;color:", ";border-radius:4px;resize:vertical;line-height:1.5rem;outline:none;border:1px solid ", ";background-color:", ";&::placeholder{color:", ";}&:hover{cursor:", ";}"], designTokens.base.color.navy2, designTokens.base.color.navy6, designTokens.base.color.navy7, designTokens.base.color.navy2, ({
23
23
  disabled
24
24
  }) => disabled ? 'not-allowed' : 'text');
25
25
  const StyledLabel = styled__default["default"].label.withConfig({
@@ -36,7 +36,8 @@ const MaterialQuizText = () => {
36
36
  const {
37
37
  onSubmit,
38
38
  onNext,
39
- refreshOrgMaterialQuiz
39
+ refreshOrgMaterialQuiz,
40
+ onDirty
40
41
  } = MaterialQuizContext.useMaterialQuizDispatch(); // state
41
42
 
42
43
  const intl = reactIntl.useIntl();
@@ -115,6 +116,7 @@ const MaterialQuizText = () => {
115
116
  void refreshOrgMaterialQuiz();
116
117
  onSubmit(true, index.getQuizResult(quizResponse));
117
118
  setSubmitStatus('resolved');
119
+ onDirty(false);
118
120
  } catch (error) {
119
121
  console.error(error);
120
122
  onSubmit(false);
@@ -133,9 +135,14 @@ const MaterialQuizText = () => {
133
135
  onChange: e => {
134
136
  setHasSubmitted(false);
135
137
  setAnswer(e.target.value);
138
+ onDirty(true);
136
139
  }
137
140
  };
138
141
  return React__default["default"].createElement(QuestionBox["default"], {
142
+ title: intl.formatMessage({
143
+ id: 'materialQuiz.text.answer.title'
144
+ }),
145
+ titlePrefix: "A.",
139
146
  onNext: onNext,
140
147
  submitResult: React__default["default"].createElement(QuizResultBadge, {
141
148
  materialQuizResponse: materialQuizResponse
@@ -13,6 +13,7 @@ export declare type MaterialQuizProps = {
13
13
  materialQuizId: number;
14
14
  userId?: number;
15
15
  locale?: string;
16
+ onDirty: (isDirty: boolean) => void;
16
17
  onSubmit?: (isSucceeded: boolean, isCorrect?: boolean) => void;
17
18
  onNext?: () => void;
18
19
  };
@@ -21,11 +22,12 @@ interface MaterialQuizProviderProps extends Omit<MaterialQuizProps, 'locale'> {
21
22
  }
22
23
  interface DispatchContextType {
23
24
  onSubmit: (isSucceeded: boolean, isCorrect?: boolean) => void;
25
+ onDirty: (isDirty: boolean) => void;
24
26
  refreshOrgMaterialQuiz: (signal?: AbortSignal | undefined) => Promise<void>;
25
27
  setVertical: React.Dispatch<React.SetStateAction<boolean>>;
26
28
  onNext?: () => void;
27
29
  }
28
- declare function MaterialQuizProvider({ materialQuizId, userId, onSubmit, onNext, children, }: MaterialQuizProviderProps): JSX.Element;
30
+ declare function MaterialQuizProvider({ materialQuizId, userId, onSubmit, onDirty, onNext, children, }: MaterialQuizProviderProps): JSX.Element;
29
31
  declare function useMaterialQuizState(): State;
30
32
  declare function useMaterialQuizDispatch(): DispatchContextType;
31
33
  export { MaterialQuizProvider, useMaterialQuizState, useMaterialQuizDispatch };
@@ -19,6 +19,7 @@ function MaterialQuizProvider({
19
19
  materialQuizId,
20
20
  userId,
21
21
  onSubmit = () => void 0,
22
+ onDirty,
22
23
  onNext,
23
24
  children
24
25
  }) {
@@ -102,6 +103,7 @@ function MaterialQuizProvider({
102
103
  }, React__default["default"].createElement(DispatchContext.Provider, {
103
104
  value: {
104
105
  onSubmit,
106
+ onDirty,
105
107
  onNext,
106
108
  refreshOrgMaterialQuiz,
107
109
  setVertical
@@ -35,7 +35,8 @@ const MaterialQuizGroup = () => {
35
35
  const {
36
36
  onSubmit,
37
37
  onNext,
38
- refreshOrgMaterialQuiz
38
+ refreshOrgMaterialQuiz,
39
+ onDirty
39
40
  } = MaterialQuizContext.useMaterialQuizDispatch();
40
41
  const [groupList, setGroupList] = React__default["default"].useState([]);
41
42
  const [optionList, setOptionList] = React__default["default"].useState([]);
@@ -127,6 +128,7 @@ const MaterialQuizGroup = () => {
127
128
  setHasSubmitted(true);
128
129
  onSubmit(true, index.getQuizResult(quizResponse));
129
130
  setSubmitStatus('resolved');
131
+ onDirty(false);
130
132
  } catch (error) {
131
133
  console.error(error);
132
134
  onSubmit(false);
@@ -167,6 +169,7 @@ const MaterialQuizGroup = () => {
167
169
  answerList[dropzoneIndex] = [...answerList[dropzoneIndex], selectedAnswerOption];
168
170
  return answerList;
169
171
  });
172
+ onDirty(true);
170
173
  setHasSubmitted(false);
171
174
  };
172
175
  /**
@@ -190,6 +193,7 @@ const MaterialQuizGroup = () => {
190
193
  return [...prevAnswerList];
191
194
  });
192
195
  setCurrentOptionList(prevOptionList => [...prevOptionList, ...[selectedOption]]);
196
+ onDirty(true);
193
197
  }; //
194
198
  //
195
199
  // quiz response fetcher
@@ -287,6 +291,7 @@ const MaterialQuizGroup = () => {
287
291
  onUpdateAnswer: setCurrentAnswerList,
288
292
  onUpdateOption: setCurrentOptionList,
289
293
  onUpdateHasSubmitted: setHasSubmitted,
294
+ onDirty,
290
295
  updateCurrentAnswer,
291
296
  updateCurrentOption,
292
297
  handleAnswerReset
@@ -11,6 +11,7 @@ interface QuizGroupContextValue {
11
11
  optionList: QuizGroupOption[];
12
12
  currentOptionList: QuizGroupOption[];
13
13
  currentAnswerList: QuizGroupOption[][];
14
+ onDirty: (value: boolean) => void;
14
15
  onUpdateAnswer: (value: React.SetStateAction<QuizGroupOption[][]>) => void;
15
16
  onUpdateOption: (value: React.SetStateAction<QuizGroupOption[]>) => void;
16
17
  onUpdateHasSubmitted: (value: boolean) => void;
@@ -3,6 +3,7 @@ import type { ButtonProps } from '@elice/blocks';
3
3
  interface QuestionBoxProps {
4
4
  footerActions?: ButtonProps[];
5
5
  title?: string;
6
+ titlePrefix?: string;
6
7
  submitResult?: React.ReactNode;
7
8
  submitStatus?: React.ReactNode;
8
9
  onNext?: () => void;
@@ -26,15 +26,15 @@ const StyledQuestionBox = styled__default["default"].div.withConfig({
26
26
  }) => hasFooter ? '' : '100%');
27
27
  const StyledQuestionBoxHeader = styled__default["default"].div.withConfig({
28
28
  componentId: "sc-6qfyxj-1"
29
- })(["display:flex;justify-content:space-between;background-color:", ";padding:1rem 1.5rem;flex:none;height:3.5rem;"], designTokens.base.color.navy6);
29
+ })(["display:flex;justify-content:space-between;background-color:", ";padding:1.5rem 1.5rem 1rem;flex:none;"], designTokens.base.color.navy8);
30
30
  const StyledQuestionBoxBody = styled__default["default"].div.withConfig({
31
31
  componentId: "sc-6qfyxj-2"
32
- })(["padding:1.5rem;background-color:", ";flex:auto;", ""], designTokens.base.color.navy7, ({
32
+ })(["background-color:", ";flex:auto;", ""], designTokens.base.color.navy8, ({
33
33
  hasFooter
34
- }) => hasFooter ? 'height: 100%; overflow-y: auto;' : '');
34
+ }) => hasFooter ? 'height: 100%; padding: 0 1.5rem; overflow-y: auto;' : 'padding: 0 1.5rem 1.5rem;');
35
35
  const StyledQuestionBoxFooter = styled__default["default"].div.withConfig({
36
36
  componentId: "sc-6qfyxj-3"
37
- })(["flex:none;display:flex;justify-content:space-between;align-items:center;padding:0.75rem 1.5rem;background-color:", ";", ""], designTokens.base.color.navy6, ({
37
+ })(["flex:none;display:flex;justify-content:space-between;align-items:center;padding:1rem 1.5rem 1.5rem;background-color:", ";", ""], designTokens.base.color.navy8, ({
38
38
  vertical
39
39
  }) => {
40
40
  return vertical ? `
@@ -61,24 +61,32 @@ const QuestionBox = _a => {
61
61
  children,
62
62
  footerActions = [],
63
63
  title,
64
+ titlePrefix,
64
65
  submitResult,
65
66
  submitStatus,
66
67
  onNext,
67
68
  isNextActive
68
69
  } = _a,
69
- props = tslib.__rest(_a, ["children", "footerActions", "title", "submitResult", "submitStatus", "onNext", "isNextActive"]);
70
+ props = tslib.__rest(_a, ["children", "footerActions", "title", "titlePrefix", "submitResult", "submitStatus", "onNext", "isNextActive"]);
70
71
 
71
72
  const intl = reactIntl.useIntl();
72
73
  const {
73
74
  vertical
74
75
  } = MaterialQuizContext.useMaterialQuizState();
75
76
  const hasFooter = footerActions.length > 0;
76
- const header = React__default["default"].createElement(StyledQuestionBoxHeader, null, React__default["default"].createElement(blocks.Text, {
77
+ const header = title ? React__default["default"].createElement(StyledQuestionBoxHeader, null, React__default["default"].createElement(blocks.Flex, null, titlePrefix ? React__default["default"].createElement(blocks.Text, {
77
78
  bold: true,
79
+ size: "large",
78
80
  customStyles: {
79
- color: designTokens.base.color.navy0
81
+ color: designTokens.base.color.primary3,
82
+ marginRight: '0.5rem'
80
83
  }
81
- }, title || ' '), submitResult ? submitResult : null);
84
+ }, titlePrefix) : null, React__default["default"].createElement(blocks.Text, {
85
+ bold: true,
86
+ role: "navy0",
87
+ size: "large",
88
+ wordBreak: "break-word"
89
+ }, title)), submitResult ? React__default["default"].createElement(blocks.Box, null, submitResult) : null) : null;
82
90
  const body = React__default["default"].createElement(StyledQuestionBoxBody, {
83
91
  hasFooter: hasFooter
84
92
  }, children);
@@ -16,7 +16,7 @@ var styled__default = /*#__PURE__*/_interopDefaultLegacy(styled);
16
16
 
17
17
  const StyledQuizDraggbleOptionHandle = styled__default["default"].div.withConfig({
18
18
  componentId: "sc-19b0zkq-0"
19
- })(["display:flex;align-items:center;justify-content:center;background-color:", ";width:1.5rem;flex:none;"], designTokens.base.color.navy3);
19
+ })(["display:flex;align-items:center;justify-content:center;background-color:", ";width:1.5rem;flex:none;"], designTokens.base.color.navy5);
20
20
  const StyledQuizDraggbleOptionContent = styled__default["default"].div.withConfig({
21
21
  componentId: "sc-19b0zkq-1"
22
22
  })(["display:flex;justify-content:space-between;min-width:0;align-items:center;flex:auto;padding:0.875rem;border:", ";background-color:", ";"], ({
@@ -24,7 +24,7 @@ const StyledQuestionCheckboxOption = styled__default["default"].div.withConfig({
24
24
  return designTokens.base.color.red1;
25
25
 
26
26
  default:
27
- return checked ? designTokens.base.color.primary1 : designTokens.base.color.navy5;
27
+ return checked ? designTokens.base.color.primary1 : designTokens.base.color.navy6;
28
28
  }
29
29
  }, ({
30
30
  disabled
@@ -27,7 +27,7 @@ const StyledQuestionRadioOption = styled__default["default"].div.withConfig({
27
27
  return designTokens.base.color.black;
28
28
 
29
29
  default:
30
- return checked ? designTokens.base.color.primary1 : designTokens.base.color.navy5;
30
+ return checked ? designTokens.base.color.primary1 : designTokens.base.color.navy6;
31
31
  }
32
32
  }, ({
33
33
  disabled
@@ -4,7 +4,10 @@ export declare const en: {
4
4
  'materialQuiz.submittedAnswer': string;
5
5
  'materialQuiz.answer': string;
6
6
  'materialQuiz.next': string;
7
- 'materialQuiz.myAnswer': string;
7
+ 'materialQuiz.selectOne.answer.title': string;
8
+ 'materialQuiz.selectMultiple.answer.title': string;
9
+ 'materialQuiz.selectMultipleOrder.answer.title': string;
10
+ 'materialQuiz.text.answer.title': string;
8
11
  'materialQuiz.empty.title': string;
9
12
  'materialQuiz.empty.description': string;
10
13
  'materialQuiz.survey.empty.title': string;
@@ -4,7 +4,10 @@ const en = {
4
4
  'materialQuiz.submittedAnswer': 'Submitted Answer',
5
5
  'materialQuiz.answer': 'Answer',
6
6
  'materialQuiz.next': 'Next question >',
7
- 'materialQuiz.myAnswer': 'My answer',
7
+ 'materialQuiz.selectOne.answer.title': 'Please choose the right answer.',
8
+ 'materialQuiz.selectMultiple.answer.title': 'Please choose the right answer. (Redundant selections available)',
9
+ 'materialQuiz.selectMultipleOrder.answer.title': 'Drag and drop your answers.',
10
+ 'materialQuiz.text.answer.title': 'Please fill out the answer.',
8
11
  'materialQuiz.empty.title': 'Quiz is now preparing.',
9
12
  'materialQuiz.empty.description': 'Please check again after quiz posting!',
10
13
  'materialQuiz.survey.empty.title': 'Survey is now preparing.',
@@ -34,7 +37,10 @@ const ko = {
34
37
  'materialQuiz.submittedAnswer': '제출한 답',
35
38
  'materialQuiz.answer': '정답',
36
39
  'materialQuiz.next': '다음 문제로 이동 >',
37
- 'materialQuiz.myAnswer': ' 답안',
40
+ 'materialQuiz.selectOne.answer.title': '알맞는 답을 선택해 주세요.',
41
+ 'materialQuiz.selectMultiple.answer.title': '알맞는 답을 선택해 주세요. (중복 선택 가능)',
42
+ 'materialQuiz.selectMultipleOrder.answer.title': '답안을 드래그 앤 드롭하세요.',
43
+ 'materialQuiz.text.answer.title': '답안을 작성해 주세요.',
38
44
  'materialQuiz.empty.title': '퀴즈가 준비 중 입니다.',
39
45
  'materialQuiz.empty.description': '퀴즈 게시 후 다시 확인해주세요!',
40
46
  'materialQuiz.survey.empty.title': '설문조사 준비 중 입니다.',
@@ -47,7 +53,7 @@ const ko = {
47
53
  'materialQuiz.explanation.close': '퀴즈해설 닫기',
48
54
  'materialQuiz.explanation.show': '퀴즈 해설 보기',
49
55
  'materialQuiz.explanation.empty': '작성된 해설이 없습니다.',
50
- 'materialQuiz.text.placeholder': '답을 입력하세요.',
56
+ 'materialQuiz.text.placeholder': '답안을 작성해 주세요.',
51
57
  'materialQuiz.text.correct': '정답입니다.',
52
58
  'materialQuiz.text.wrong': '오답입니다. 다시 답안을 제출해보세요.',
53
59
  'materialQuiz.order.answerEmpty': '항목을 다시 이곳으로 옮길 수 있습니다.',
@@ -94,6 +94,7 @@ const MaterialQuizContainer = ({
94
94
  materialQuizId,
95
95
  userId,
96
96
  locale = 'en',
97
+ onDirty,
97
98
  onSubmit,
98
99
  onNext
99
100
  }) => {
@@ -104,6 +105,7 @@ const MaterialQuizContainer = ({
104
105
  }, React.createElement(MaterialQuizProvider, {
105
106
  materialQuizId: materialQuizId,
106
107
  userId: userId,
108
+ onDirty: onDirty,
107
109
  onSubmit: onSubmit,
108
110
  onNext: onNext
109
111
  }, React.createElement(MaterialQuiz, null)));
@@ -1,42 +1,35 @@
1
1
  import React from 'react';
2
- import { Flex, Text, Shimmer, Vspace } from '@elice/blocks';
3
- import { base } from '@elice/design-tokens';
2
+ import { Vspace, Shimmer } from '@elice/blocks';
4
3
  import { MarkdownSSR } from '@elice/markdown';
4
+ import styled from 'styled-components';
5
5
  import QuestionBox from '../shared/QuestionBox.js';
6
6
  import { useMaterialQuizState } from './context/MaterialQuizContext.js';
7
7
  import MaterialQuizAnswerExplanation from './MaterialQuizAnswerExplanation.js';
8
8
 
9
+ //
10
+ //
11
+
12
+ const StyledMarkdownSSR = styled(MarkdownSSR).withConfig({
13
+ componentId: "sc-1s7jbf9-0"
14
+ })([".elicemd--theme-dark{pre{border:0;}table td,table th{border-bottom-color:rgba(255,255,255,0.16);}}"]); //
15
+ //
16
+ //
17
+
9
18
  const MaterialQuizInfo = () => {
10
19
  const {
11
20
  materialQuiz
12
21
  } = useMaterialQuizState();
13
22
  return React.createElement(QuestionBox, {
14
- title: materialQuiz === null || materialQuiz === void 0 ? void 0 : materialQuiz.title
15
- }, materialQuiz ? React.createElement(Flex, null, React.createElement(Text, {
16
- bold: true,
17
- size: "large",
18
- customStyles: {
19
- color: base.color.primary3,
20
- marginRight: '0.5rem'
21
- }
22
- }, 'Q.'), React.createElement(Text, {
23
- bold: true,
24
- role: "white",
25
- size: "large",
26
- wordBreak: "break-word"
27
- }, materialQuiz.questionTitle)) : React.createElement(Shimmer, {
28
- dark: true,
29
- borderRadius: '4px',
30
- width: "80%",
31
- height: '40px'
32
- }), React.createElement(Vspace, {
33
- height: 1
34
- }), materialQuiz ? React.createElement(MarkdownSSR, {
23
+ title: materialQuiz === null || materialQuiz === void 0 ? void 0 : materialQuiz.questionTitle,
24
+ titlePrefix: "Q."
25
+ }, materialQuiz ? React.createElement(StyledMarkdownSSR, {
35
26
  children: materialQuiz.questionDescription,
36
27
  dark: true,
37
28
  paddingx: 0,
38
29
  paddingy: 0
39
- }) : React.createElement(React.Fragment, null, React.createElement(Shimmer, {
30
+ }) : React.createElement(React.Fragment, null, React.createElement(Vspace, {
31
+ height: 1
32
+ }), React.createElement(Shimmer, {
40
33
  dark: true,
41
34
  borderRadius: '4px',
42
35
  width: "80%",
@@ -25,7 +25,8 @@ const MaterialQuizSelectMultiple = () => {
25
25
  const {
26
26
  onSubmit,
27
27
  onNext,
28
- refreshOrgMaterialQuiz
28
+ refreshOrgMaterialQuiz,
29
+ onDirty
29
30
  } = useMaterialQuizDispatch(); // state
30
31
 
31
32
  const intl = useIntl();
@@ -34,7 +35,22 @@ const MaterialQuizSelectMultiple = () => {
34
35
  const [submitStatus, setSubmitStatus] = React.useState('idle'); // Whether user has clicked any options
35
36
 
36
37
  const [isActive, setIsActive] = React.useState(false);
37
- const [hasSubmitted, setHasSubmitted] = React.useState(false); // quiz response fetcher
38
+ const [hasSubmitted, setHasSubmitted] = React.useState(false); // answer select handler
39
+
40
+ const handleAnswerSelect = index => {
41
+ if (checkUserLectureTestEnded(lecture) || !!userId) {
42
+ return;
43
+ }
44
+
45
+ onDirty(true);
46
+ setHasSubmitted(false);
47
+ setIsActive(true);
48
+ setSelectedAnswer(prevState => {
49
+ const newState = prevState.includes(index) ? prevState.filter(ident => ident !== index) : [...prevState, index];
50
+ return newState;
51
+ });
52
+ }; // quiz response fetcher
53
+
38
54
 
39
55
  React.useEffect(() => {
40
56
  if (!userId && (materialQuiz === null || materialQuiz === void 0 ? void 0 : materialQuiz.lastQuizResponseId)) {
@@ -119,6 +135,7 @@ const MaterialQuizSelectMultiple = () => {
119
135
  onSubmit(true, getQuizResult(quizResponse));
120
136
  setSubmitStatus('resolved');
121
137
  setHasSubmitted(true);
138
+ onDirty(false);
122
139
  } catch (error) {
123
140
  console.error(error);
124
141
  onSubmit(false);
@@ -128,8 +145,9 @@ const MaterialQuizSelectMultiple = () => {
128
145
 
129
146
  return React.createElement(QuestionBox, {
130
147
  title: intl.formatMessage({
131
- id: 'materialQuiz.myAnswer'
148
+ id: 'materialQuiz.selectMultiple.answer.title'
132
149
  }),
150
+ titlePrefix: "A.",
133
151
  onNext: onNext,
134
152
  isNextActive: hasSubmitted && typeof onNext === 'function',
135
153
  submitResult: React.createElement(QuizResultBadge, {
@@ -151,18 +169,7 @@ const MaterialQuizSelectMultiple = () => {
151
169
  })
152
170
  }]
153
171
  }, React.createElement(QuestionCheckbox, {
154
- onSelect: index => {
155
- if (checkUserLectureTestEnded(lecture) || !!userId) {
156
- return;
157
- }
158
-
159
- setHasSubmitted(false);
160
- setIsActive(true);
161
- setSelectedAnswer(prevState => {
162
- const newState = prevState.includes(index) ? prevState.filter(ident => ident !== index) : [...prevState, index];
163
- return newState;
164
- });
165
- },
172
+ onSelect: handleAnswerSelect,
166
173
  selectedOptions: selectedAnswer,
167
174
  disabled: submitStatus === 'pending' || checkUserLectureTestEnded(lecture) || !!userId
168
175
  }, (materialQuiz === null || materialQuiz === void 0 ? void 0 : materialQuiz.options) ? materialQuiz.options.map((option, index) => {
@@ -38,7 +38,7 @@ const StyledHorizontalDivider = styled.hr.withConfig({
38
38
  });
39
39
  const StyledQuizOptionsDropZone = styled(EbDroppable).withConfig({
40
40
  componentId: "sc-10067nt-2"
41
- })(["display:flex;flex-direction:column;flex:1 1 50%;padding:1rem;border-radius:8px;background-color:", ";& > div{margin-bottom:1rem;}&.ui-droppable-hover{outline:1px dashed ", ";}"], base.color.navy6, base.color.gray4);
41
+ })(["display:flex;flex-direction:column;flex:1 1 50%;padding:1rem;border-radius:8px;background-color:", ";& > div{margin-bottom:1rem;}&.ui-droppable-hover{outline:1px dashed ", ";}"], base.color.navy7, base.color.gray4);
42
42
  const StyledQuizAnswersDropZone = styled.div.withConfig({
43
43
  componentId: "sc-10067nt-3"
44
44
  })(["position:relative;display:flex;flex-direction:column;flex:1 1 50%;padding:1rem;border-radius:8px;background-color:", ";& > div{margin-bottom:1rem;}"], base.color.navy6);
@@ -64,7 +64,8 @@ const MaterialQuizSelectMultipleOrder = () => {
64
64
  const {
65
65
  onSubmit,
66
66
  onNext,
67
- refreshOrgMaterialQuiz
67
+ refreshOrgMaterialQuiz,
68
+ onDirty
68
69
  } = useMaterialQuizDispatch(); // state
69
70
 
70
71
  const intl = useIntl();
@@ -279,6 +280,7 @@ const MaterialQuizSelectMultipleOrder = () => {
279
280
  void refreshOrgMaterialQuiz();
280
281
  onSubmit(true, getQuizResult(quizResponse));
281
282
  setSubmitStatus('resolved');
283
+ onDirty(false);
282
284
  } catch (error) {
283
285
  console.error(error);
284
286
  onSubmit(false);
@@ -370,7 +372,8 @@ const MaterialQuizSelectMultipleOrder = () => {
370
372
  }
371
373
 
372
374
  setIsActive(true);
373
- setHasSubmitted(false); // from answerList to answerList
375
+ setHasSubmitted(false);
376
+ onDirty(true); // from answerList to answerList
374
377
 
375
378
  if (ui.draggable[0].classList.contains('quiz-answer')) {
376
379
  const targetOptionId = Number(ui.draggable[0].id);
@@ -422,8 +425,9 @@ const MaterialQuizSelectMultipleOrder = () => {
422
425
 
423
426
  return React.createElement(QuestionBox, {
424
427
  title: intl.formatMessage({
425
- id: 'materialQuiz.myAnswer'
428
+ id: 'materialQuiz.selectMultipleOrder.answer.title'
426
429
  }),
430
+ titlePrefix: "A.",
427
431
  onNext: onNext,
428
432
  isNextActive: hasSubmitted && typeof onNext === 'function',
429
433
  submitStatus: React.createElement(QuizSubmitStatusText, {
@@ -25,7 +25,8 @@ const MaterialQuizSelectOne = () => {
25
25
  const {
26
26
  onSubmit,
27
27
  onNext,
28
- refreshOrgMaterialQuiz
28
+ refreshOrgMaterialQuiz,
29
+ onDirty
29
30
  } = useMaterialQuizDispatch(); // state
30
31
 
31
32
  const intl = useIntl();
@@ -34,7 +35,19 @@ const MaterialQuizSelectOne = () => {
34
35
  const [submitStatus, setSubmitStatus] = React.useState('idle'); // Whether user has clicked any options
35
36
 
36
37
  const [isActive, setIsActive] = React.useState(false);
37
- const [hasSubmitted, setHasSubmitted] = React.useState(false); // quiz response fetcher
38
+ const [hasSubmitted, setHasSubmitted] = React.useState(false); // answer select handler
39
+
40
+ const handleAnswerSelect = index => {
41
+ if (checkUserLectureTestEnded(lecture) || !!userId) {
42
+ return;
43
+ }
44
+
45
+ setHasSubmitted(false);
46
+ setIsActive(true);
47
+ setSelectedAnswer(index);
48
+ onDirty(true);
49
+ }; // quiz response fetcher
50
+
38
51
 
39
52
  React.useEffect(() => {
40
53
  if (!userId && (materialQuiz === null || materialQuiz === void 0 ? void 0 : materialQuiz.lastQuizResponseId)) {
@@ -109,6 +122,7 @@ const MaterialQuizSelectOne = () => {
109
122
  onSubmit(true, getQuizResult(quizResponse));
110
123
  setSubmitStatus('resolved');
111
124
  setHasSubmitted(true);
125
+ onDirty(false);
112
126
  } catch (error) {
113
127
  console.error(error);
114
128
  onSubmit(false);
@@ -118,8 +132,9 @@ const MaterialQuizSelectOne = () => {
118
132
 
119
133
  return React.createElement(QuestionBox, {
120
134
  title: intl.formatMessage({
121
- id: 'materialQuiz.myAnswer'
135
+ id: 'materialQuiz.selectOne.answer.title'
122
136
  }),
137
+ titlePrefix: "A.",
123
138
  onNext: onNext,
124
139
  isNextActive: hasSubmitted && typeof onNext === 'function',
125
140
  submitStatus: React.createElement(QuizSubmitStatusText, {
@@ -142,15 +157,7 @@ const MaterialQuizSelectOne = () => {
142
157
  }]
143
158
  }, materialQuiz ? React.createElement(QuestionRadio, {
144
159
  selectedValue: selectedAnswer,
145
- onSelect: index => {
146
- if (checkUserLectureTestEnded(lecture) || !!userId) {
147
- return;
148
- }
149
-
150
- setHasSubmitted(false);
151
- setIsActive(true);
152
- setSelectedAnswer(index);
153
- },
160
+ onSelect: handleAnswerSelect,
154
161
  disabled: submitStatus === 'pending' || checkUserLectureTestEnded(lecture) || !!userId
155
162
  }, materialQuiz.options ? materialQuiz.options.map((option, index) => {
156
163
  var _a, _b;
@@ -163,7 +170,7 @@ const MaterialQuizSelectOne = () => {
163
170
  }
164
171
 
165
172
  const materialQuizResponseAnswer = materialQuizResponse.answer;
166
- return (_a = materialQuizResponseAnswer.includes(index)) !== null && _a !== void 0 ? _a : false;
173
+ return (_a = materialQuizResponseAnswer === null || materialQuizResponseAnswer === void 0 ? void 0 : materialQuizResponseAnswer.includes(index)) !== null && _a !== void 0 ? _a : false;
167
174
  };
168
175
 
169
176
  const status = getOptionStatus({
@@ -9,12 +9,9 @@ const StyledShimmer = styled.div.withConfig({
9
9
  })(["display:flex;flex-direction:column;border-radius:", ";overflow:hidden;width:100%;"], ({
10
10
  vertical
11
11
  }) => vertical ? '0' : '8px');
12
- const StyledShimmerHeader = styled.div.withConfig({
13
- componentId: "sc-10uz5tw-1"
14
- })(["display:flex;justify-content:space-between;background-color:", ";padding:1rem 1.5rem;height:3.5rem;"], base.color.navy6);
15
12
  const StyledShimmerBody = styled.div.withConfig({
16
- componentId: "sc-10uz5tw-2"
17
- })(["padding:1.5rem;background-color:", ";overflow-y:auto;height:100%;"], base.color.navy7);
13
+ componentId: "sc-10uz5tw-1"
14
+ })(["padding:1.5rem;background-color:", ";overflow-y:auto;height:100%;"], base.color.navy8);
18
15
 
19
16
  const MaterialQuizShimmer = () => {
20
17
  const {
@@ -22,7 +19,7 @@ const MaterialQuizShimmer = () => {
22
19
  } = useMaterialQuizState();
23
20
  return React.createElement(StyledShimmer, {
24
21
  vertical: vertical
25
- }, React.createElement(StyledShimmerHeader, null), React.createElement(StyledShimmerBody, null, React.createElement(Shimmer, {
22
+ }, React.createElement(StyledShimmerBody, null, React.createElement(Shimmer, {
26
23
  dark: true,
27
24
  borderRadius: '4px',
28
25
  width: "80%",
@@ -12,7 +12,7 @@ import QuizSubmitStatusText from './QuizSubmitStatusText.js';
12
12
 
13
13
  const StyledTextarea = styled.textarea.withConfig({
14
14
  componentId: "sc-186cnpj-0"
15
- })(["min-height:12rem;width:100%;padding:1rem 1.375rem;font-size:1rem;color:", ";border-radius:4px;resize:vertical;line-height:1.5rem;outline:none;background-color:", ";&::placeholder{color:", ";}&:hover{cursor:", ";}"], base.color.navy2, base.color.navy5, base.color.navy2, ({
15
+ })(["min-height:12rem;width:100%;padding:1rem 1.375rem;font-size:1rem;color:", ";border-radius:4px;resize:vertical;line-height:1.5rem;outline:none;border:1px solid ", ";background-color:", ";&::placeholder{color:", ";}&:hover{cursor:", ";}"], base.color.navy2, base.color.navy6, base.color.navy7, base.color.navy2, ({
16
16
  disabled
17
17
  }) => disabled ? 'not-allowed' : 'text');
18
18
  const StyledLabel = styled.label.withConfig({
@@ -29,7 +29,8 @@ const MaterialQuizText = () => {
29
29
  const {
30
30
  onSubmit,
31
31
  onNext,
32
- refreshOrgMaterialQuiz
32
+ refreshOrgMaterialQuiz,
33
+ onDirty
33
34
  } = useMaterialQuizDispatch(); // state
34
35
 
35
36
  const intl = useIntl();
@@ -108,6 +109,7 @@ const MaterialQuizText = () => {
108
109
  void refreshOrgMaterialQuiz();
109
110
  onSubmit(true, getQuizResult(quizResponse));
110
111
  setSubmitStatus('resolved');
112
+ onDirty(false);
111
113
  } catch (error) {
112
114
  console.error(error);
113
115
  onSubmit(false);
@@ -126,9 +128,14 @@ const MaterialQuizText = () => {
126
128
  onChange: e => {
127
129
  setHasSubmitted(false);
128
130
  setAnswer(e.target.value);
131
+ onDirty(true);
129
132
  }
130
133
  };
131
134
  return React.createElement(QuestionBox, {
135
+ title: intl.formatMessage({
136
+ id: 'materialQuiz.text.answer.title'
137
+ }),
138
+ titlePrefix: "A.",
132
139
  onNext: onNext,
133
140
  submitResult: React.createElement(QuizResultBadge, {
134
141
  materialQuizResponse: materialQuizResponse
@@ -13,6 +13,7 @@ export declare type MaterialQuizProps = {
13
13
  materialQuizId: number;
14
14
  userId?: number;
15
15
  locale?: string;
16
+ onDirty: (isDirty: boolean) => void;
16
17
  onSubmit?: (isSucceeded: boolean, isCorrect?: boolean) => void;
17
18
  onNext?: () => void;
18
19
  };
@@ -21,11 +22,12 @@ interface MaterialQuizProviderProps extends Omit<MaterialQuizProps, 'locale'> {
21
22
  }
22
23
  interface DispatchContextType {
23
24
  onSubmit: (isSucceeded: boolean, isCorrect?: boolean) => void;
25
+ onDirty: (isDirty: boolean) => void;
24
26
  refreshOrgMaterialQuiz: (signal?: AbortSignal | undefined) => Promise<void>;
25
27
  setVertical: React.Dispatch<React.SetStateAction<boolean>>;
26
28
  onNext?: () => void;
27
29
  }
28
- declare function MaterialQuizProvider({ materialQuizId, userId, onSubmit, onNext, children, }: MaterialQuizProviderProps): JSX.Element;
30
+ declare function MaterialQuizProvider({ materialQuizId, userId, onSubmit, onDirty, onNext, children, }: MaterialQuizProviderProps): JSX.Element;
29
31
  declare function useMaterialQuizState(): State;
30
32
  declare function useMaterialQuizDispatch(): DispatchContextType;
31
33
  export { MaterialQuizProvider, useMaterialQuizState, useMaterialQuizDispatch };
@@ -11,6 +11,7 @@ function MaterialQuizProvider({
11
11
  materialQuizId,
12
12
  userId,
13
13
  onSubmit = () => void 0,
14
+ onDirty,
14
15
  onNext,
15
16
  children
16
17
  }) {
@@ -94,6 +95,7 @@ function MaterialQuizProvider({
94
95
  }, React.createElement(DispatchContext.Provider, {
95
96
  value: {
96
97
  onSubmit,
98
+ onDirty,
97
99
  onNext,
98
100
  refreshOrgMaterialQuiz,
99
101
  setVertical
@@ -27,7 +27,8 @@ const MaterialQuizGroup = () => {
27
27
  const {
28
28
  onSubmit,
29
29
  onNext,
30
- refreshOrgMaterialQuiz
30
+ refreshOrgMaterialQuiz,
31
+ onDirty
31
32
  } = useMaterialQuizDispatch();
32
33
  const [groupList, setGroupList] = React.useState([]);
33
34
  const [optionList, setOptionList] = React.useState([]);
@@ -119,6 +120,7 @@ const MaterialQuizGroup = () => {
119
120
  setHasSubmitted(true);
120
121
  onSubmit(true, getQuizResult(quizResponse));
121
122
  setSubmitStatus('resolved');
123
+ onDirty(false);
122
124
  } catch (error) {
123
125
  console.error(error);
124
126
  onSubmit(false);
@@ -159,6 +161,7 @@ const MaterialQuizGroup = () => {
159
161
  answerList[dropzoneIndex] = [...answerList[dropzoneIndex], selectedAnswerOption];
160
162
  return answerList;
161
163
  });
164
+ onDirty(true);
162
165
  setHasSubmitted(false);
163
166
  };
164
167
  /**
@@ -182,6 +185,7 @@ const MaterialQuizGroup = () => {
182
185
  return [...prevAnswerList];
183
186
  });
184
187
  setCurrentOptionList(prevOptionList => [...prevOptionList, ...[selectedOption]]);
188
+ onDirty(true);
185
189
  }; //
186
190
  //
187
191
  // quiz response fetcher
@@ -279,6 +283,7 @@ const MaterialQuizGroup = () => {
279
283
  onUpdateAnswer: setCurrentAnswerList,
280
284
  onUpdateOption: setCurrentOptionList,
281
285
  onUpdateHasSubmitted: setHasSubmitted,
286
+ onDirty,
282
287
  updateCurrentAnswer,
283
288
  updateCurrentOption,
284
289
  handleAnswerReset
@@ -11,6 +11,7 @@ interface QuizGroupContextValue {
11
11
  optionList: QuizGroupOption[];
12
12
  currentOptionList: QuizGroupOption[];
13
13
  currentAnswerList: QuizGroupOption[][];
14
+ onDirty: (value: boolean) => void;
14
15
  onUpdateAnswer: (value: React.SetStateAction<QuizGroupOption[][]>) => void;
15
16
  onUpdateOption: (value: React.SetStateAction<QuizGroupOption[]>) => void;
16
17
  onUpdateHasSubmitted: (value: boolean) => void;
@@ -3,6 +3,7 @@ import type { ButtonProps } from '@elice/blocks';
3
3
  interface QuestionBoxProps {
4
4
  footerActions?: ButtonProps[];
5
5
  title?: string;
6
+ titlePrefix?: string;
6
7
  submitResult?: React.ReactNode;
7
8
  submitStatus?: React.ReactNode;
8
9
  onNext?: () => void;
@@ -1,7 +1,7 @@
1
1
  import { __rest } from 'tslib';
2
2
  import React from 'react';
3
3
  import { useIntl } from 'react-intl';
4
- import { Text, Button, Box } from '@elice/blocks';
4
+ import { Flex, Text, Box, Button } from '@elice/blocks';
5
5
  import { base } from '@elice/design-tokens';
6
6
  import styled from 'styled-components';
7
7
  import { useMaterialQuizState } from '../material-quiz/context/MaterialQuizContext.js';
@@ -17,15 +17,15 @@ const StyledQuestionBox = styled.div.withConfig({
17
17
  }) => hasFooter ? '' : '100%');
18
18
  const StyledQuestionBoxHeader = styled.div.withConfig({
19
19
  componentId: "sc-6qfyxj-1"
20
- })(["display:flex;justify-content:space-between;background-color:", ";padding:1rem 1.5rem;flex:none;height:3.5rem;"], base.color.navy6);
20
+ })(["display:flex;justify-content:space-between;background-color:", ";padding:1.5rem 1.5rem 1rem;flex:none;"], base.color.navy8);
21
21
  const StyledQuestionBoxBody = styled.div.withConfig({
22
22
  componentId: "sc-6qfyxj-2"
23
- })(["padding:1.5rem;background-color:", ";flex:auto;", ""], base.color.navy7, ({
23
+ })(["background-color:", ";flex:auto;", ""], base.color.navy8, ({
24
24
  hasFooter
25
- }) => hasFooter ? 'height: 100%; overflow-y: auto;' : '');
25
+ }) => hasFooter ? 'height: 100%; padding: 0 1.5rem; overflow-y: auto;' : 'padding: 0 1.5rem 1.5rem;');
26
26
  const StyledQuestionBoxFooter = styled.div.withConfig({
27
27
  componentId: "sc-6qfyxj-3"
28
- })(["flex:none;display:flex;justify-content:space-between;align-items:center;padding:0.75rem 1.5rem;background-color:", ";", ""], base.color.navy6, ({
28
+ })(["flex:none;display:flex;justify-content:space-between;align-items:center;padding:1rem 1.5rem 1.5rem;background-color:", ";", ""], base.color.navy8, ({
29
29
  vertical
30
30
  }) => {
31
31
  return vertical ? `
@@ -52,24 +52,32 @@ const QuestionBox = _a => {
52
52
  children,
53
53
  footerActions = [],
54
54
  title,
55
+ titlePrefix,
55
56
  submitResult,
56
57
  submitStatus,
57
58
  onNext,
58
59
  isNextActive
59
60
  } = _a,
60
- props = __rest(_a, ["children", "footerActions", "title", "submitResult", "submitStatus", "onNext", "isNextActive"]);
61
+ props = __rest(_a, ["children", "footerActions", "title", "titlePrefix", "submitResult", "submitStatus", "onNext", "isNextActive"]);
61
62
 
62
63
  const intl = useIntl();
63
64
  const {
64
65
  vertical
65
66
  } = useMaterialQuizState();
66
67
  const hasFooter = footerActions.length > 0;
67
- const header = React.createElement(StyledQuestionBoxHeader, null, React.createElement(Text, {
68
+ const header = title ? React.createElement(StyledQuestionBoxHeader, null, React.createElement(Flex, null, titlePrefix ? React.createElement(Text, {
68
69
  bold: true,
70
+ size: "large",
69
71
  customStyles: {
70
- color: base.color.navy0
72
+ color: base.color.primary3,
73
+ marginRight: '0.5rem'
71
74
  }
72
- }, title || ' '), submitResult ? submitResult : null);
75
+ }, titlePrefix) : null, React.createElement(Text, {
76
+ bold: true,
77
+ role: "navy0",
78
+ size: "large",
79
+ wordBreak: "break-word"
80
+ }, title)), submitResult ? React.createElement(Box, null, submitResult) : null) : null;
73
81
  const body = React.createElement(StyledQuestionBoxBody, {
74
82
  hasFooter: hasFooter
75
83
  }, children);
@@ -9,7 +9,7 @@ import StyledMarkdown from './StyledMarkdown.js';
9
9
 
10
10
  const StyledQuizDraggbleOptionHandle = styled.div.withConfig({
11
11
  componentId: "sc-19b0zkq-0"
12
- })(["display:flex;align-items:center;justify-content:center;background-color:", ";width:1.5rem;flex:none;"], base.color.navy3);
12
+ })(["display:flex;align-items:center;justify-content:center;background-color:", ";width:1.5rem;flex:none;"], base.color.navy5);
13
13
  const StyledQuizDraggbleOptionContent = styled.div.withConfig({
14
14
  componentId: "sc-19b0zkq-1"
15
15
  })(["display:flex;justify-content:space-between;min-width:0;align-items:center;flex:auto;padding:0.875rem;border:", ";background-color:", ";"], ({
@@ -17,7 +17,7 @@ const StyledQuestionCheckboxOption = styled.div.withConfig({
17
17
  return base.color.red1;
18
18
 
19
19
  default:
20
- return checked ? base.color.primary1 : base.color.navy5;
20
+ return checked ? base.color.primary1 : base.color.navy6;
21
21
  }
22
22
  }, ({
23
23
  disabled
@@ -20,7 +20,7 @@ const StyledQuestionRadioOption = styled.div.withConfig({
20
20
  return base.color.black;
21
21
 
22
22
  default:
23
- return checked ? base.color.primary1 : base.color.navy5;
23
+ return checked ? base.color.primary1 : base.color.navy6;
24
24
  }
25
25
  }, ({
26
26
  disabled
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elice/material-quiz",
3
- "version": "1.230203.0",
3
+ "version": "1.230208.0-style.0",
4
4
  "description": "User view and editing components of Elice material quiz",
5
5
  "repository": "https://git.elicer.io/elice/frontend/library/elice-material",
6
6
  "license": "UNLICENSED",
@@ -58,8 +58,8 @@
58
58
  "@elice/icons": "^1.220803.0",
59
59
  "@elice/icons-legacy": "npm:@elice/icons@0.220803.1",
60
60
  "@elice/markdown": "^1.220803.0",
61
- "@elice/material-shared-types": "1.230203.0",
62
- "@elice/material-shared-utils": "1.230203.0",
61
+ "@elice/material-shared-types": "1.230208.0-style.0",
62
+ "@elice/material-shared-utils": "1.230208.0-style.0",
63
63
  "@elice/types": "1.221217.0",
64
64
  "@types/classnames": "^2.3.1",
65
65
  "@types/jquery": "^3.5.13",
@@ -73,5 +73,5 @@
73
73
  "react-use": "^17.2.4",
74
74
  "styled-components": "^5.3.0"
75
75
  },
76
- "gitHead": "ffbc8a681faabee9aca717331ad39bec304edc16"
76
+ "gitHead": "ebcb69994e735767be76506db929bac91ab91394"
77
77
  }