diff --git a/jest.init.ts b/jest.init.ts
index decc871..750fd09 100644
--- a/jest.init.ts
+++ b/jest.init.ts
@@ -7,12 +7,33 @@
import { config } from "@vue/test-utils";
import VueClickAway from "vue3-click-away";
+// Mock window.matchMedia
+Object.defineProperty(window, 'matchMedia', {
+ writable: true,
+ value: (query: string) => ({
+ matches: false,
+ media: query,
+ onchange: null,
+ addListener: jest.fn(), // deprecated
+ removeListener: jest.fn(), // deprecated
+ addEventListener: jest.fn(),
+ removeEventListener: jest.fn(),
+ dispatchEvent: jest.fn(),
+ }),
+});
+
+// Mock window.scrollTo
+Object.defineProperty(window, 'scrollTo', {
+ writable: true,
+ value: jest.fn(),
+});
+
// inline-svg stub
const InlineSvg = {
template: "",
};
config.global.stubs = {
- InlineSvg: InlineSvg,
+ InlineSvg,
};
config.global.plugins = [VueClickAway];
diff --git a/src/components/InstructionPage.vue b/src/components/InstructionPage.vue
index 4f5289a..4f4e700 100644
--- a/src/components/InstructionPage.vue
+++ b/src/components/InstructionPage.vue
@@ -54,27 +54,48 @@
@@ -95,6 +107,8 @@ import {
toRefs,
computed,
PropType,
+ onMounted,
+ onBeforeUnmount
} from "vue";
export default defineComponent({
@@ -108,6 +122,10 @@ export default defineComponent({
default: false,
type: Boolean,
},
+ isMarkedForReview: {
+ default: false,
+ type: Boolean,
+ },
isPreviousButtonShown: {
default: false,
type: Boolean,
@@ -139,6 +157,10 @@ export default defineComponent({
},
setup(props, context) {
const isQuizAssessment = computed(() => props.quizType == "assessment" || props.quizType == "omr-assessment");
+ const isSmallScreen = ref(false);
+ const updateScreenSize = () => {
+ isSmallScreen.value = window.matchMedia("(max-width: 500px)").matches;
+ };
const state = reactive({
assessmentNavigationButtonIconClass: [
{
@@ -149,7 +171,7 @@ export default defineComponent({
"fill-current",
],
assessmentTextButtonTitleClass:
- "text-sm bp-500:text-md lg:text-lg xl:text-xl font-bold",
+ "text-xs bp-500:text-sm lg:text-base xl:text-lg font-bold",
assessmentNavigationButtonClass: [
{
"bg-yellow-500 hover:bg-yellow-600 ring-yellow-500 px-6 bp-500:px-8 rounded-2xl":
@@ -183,6 +205,11 @@ export default defineComponent({
"bg-white hover:bg-gray-50",
]);
+ const markForReviewButtonClass = ref([
+ state.assessmentTextButtonClass,
+ "bg-white hover:bg-gray-50",
+ ]);
+
const saveAndNextButtonClass = ref([
state.assessmentTextButtonClass,
"bg-white hover:bg-gray-50",
@@ -193,6 +220,11 @@ export default defineComponent({
class: [state.assessmentTextButtonTitleClass, "text-gray-600"],
} as IconButtonTitleConfig);
+ const markForReviewButtonTitleConfig = computed(() => ({
+ value: isSmallScreen.value ? "Review >" : "Mark For Review & Next",
+ class: ["text-xxs bp-500:text-sm lg:text-base xl:text-lg font-bold", "text-violet-500"],
+ } as IconButtonTitleConfig));
+
const saveAndNextButtonTitleConfig = ref({
value: "Save & Next",
class: [state.assessmentTextButtonTitleClass, "text-emerald-500"],
@@ -216,6 +248,19 @@ export default defineComponent({
context.emit("clear");
}
+ function toggleMarkForReview() {
+ if (!props.isMarkedForReview) {
+ context.emit("mark-for-review");
+ // wait for processing to be done and answer to be submitted
+ const checkProcessingDone = setInterval(() => {
+ if (!props.isSessionAnswerRequestProcessing) {
+ if (props.continueAfterAnswerSubmit) context.emit("continue");
+ clearInterval(checkProcessingDone);
+ }
+ }, 100); // check every 100ms
+ }
+ }
+
function goToPreviousQuestion() {
context.emit("previous");
}
@@ -262,19 +307,32 @@ export default defineComponent({
"p-4 px-8 bp-500:p-6 bp-500:px-12 rounded-2xl shadow-xl disabled:opacity-50 disabled:cursor-not-allowed",
]);
+ // Setup listeners for screen size changes
+ onMounted(() => {
+ updateScreenSize();
+ window.addEventListener("resize", updateScreenSize);
+ });
+
+ onBeforeUnmount(() => {
+ window.removeEventListener("resize", updateScreenSize);
+ });
+
return {
...toRefs(state),
previousQuestionButtonIconConfig,
nextQuestionButtonIconConfig,
clearButtonClass,
+ markForReviewButtonClass,
saveAndNextButtonClass,
clearButtonTitleConfig,
+ markForReviewButtonTitleConfig,
saveAndNextButtonTitleConfig,
saveAndNextButtonIconConfig,
submitQuestion,
goToPreviousQuestion,
goToNextQuestion,
clearAnswer,
+ toggleMarkForReview,
saveQuestionAndProceed,
submitButtonTitleConfig,
submitButtonIconConfig,
@@ -282,6 +340,6 @@ export default defineComponent({
isQuizAssessment,
};
},
- emits: ["submit", "previous", "continue", "clear"],
+ emits: ["submit", "previous", "continue", "clear", "mark-for-review"],
});
diff --git a/src/components/Questions/Palette/Item.vue b/src/components/Questions/Palette/Item.vue
index 4271e13..cff2c5b 100644
--- a/src/components/Questions/Palette/Item.vue
+++ b/src/components/Questions/Palette/Item.vue
@@ -20,6 +20,11 @@
:hasQuizEnded="hasQuizEnded"
data-test="neutral"
>
+
+
+
+
@@ -78,6 +84,7 @@ import Success from "./Success.vue";
import PartialSuccess from "./PartialSuccess.vue";
import Error from "./Error.vue";
import Neutral from "./Neutral.vue";
+import Review from "./Review.vue";
import PaletteItem from "./Item.vue";
import InstructionPage from "@/components/InstructionPage.vue";
import { TimeLimit, questionSetPalette, quizTitleType, testFormat } from "@/types";
@@ -89,6 +96,7 @@ export default defineComponent({
PartialSuccess,
Error,
Neutral,
+ Review,
PaletteItem,
InstructionPage
},
diff --git a/src/components/Questions/Palette/Review.vue b/src/components/Questions/Palette/Review.vue
new file mode 100644
index 0000000..7c55868
--- /dev/null
+++ b/src/components/Questions/Palette/Review.vue
@@ -0,0 +1,29 @@
+
+
+
+
+
diff --git a/src/components/Questions/Palette/Utils.ts b/src/components/Questions/Palette/Utils.ts
index 7a0db84..f355e81 100644
--- a/src/components/Questions/Palette/Utils.ts
+++ b/src/components/Questions/Palette/Utils.ts
@@ -1,8 +1,8 @@
// classes for the icon of each key in the legend of the question palette
export const legendKeyIconClass =
- "w-6 h-6 bp-420:w-10 bp-420:h-10 border-1 rounded-md place-self-center";
+ "w-6 h-6 bp-420:w-10 bp-420:h-10 border-1 rounded-md place-self-center flex-shrink-0";
// classes for the text of each key in the legend of the question palette
export const legendKeyTextClass =
"place-self-center text-sm sm:text-base font-bold";
// classes for the container of each key in the legend of the question palette
-export const legendKeyContainerClass = "flex space-x-2 lg:space-x-4";
+export const legendKeyContainerClass = "flex space-x-2 lg:space-x-4 items-centers";
diff --git a/src/components/Questions/QuestionModal.vue b/src/components/Questions/QuestionModal.vue
index 2735a98..7f9b570 100644
--- a/src/components/Questions/QuestionModal.vue
+++ b/src/components/Questions/QuestionModal.vue
@@ -47,6 +47,8 @@
:draftAnswer="draftResponses[currentQuestionIndex]"
:submittedAnswer="currentQuestionResponseAnswer"
:isAnswerSubmitted="isAnswerSubmitted"
+ :isMarkedForReview="isMarkedForReview"
+ :isSessionAnswerRequestProcessing="$props.isSessionAnswerRequestProcessing"
:isPaletteVisible="isPaletteVisible"
:isDraftAnswerCleared="isDraftAnswerCleared"
:quizType="quizType"
@@ -72,6 +74,7 @@
>