Skip to content

Commit

Permalink
Merge pull request #150 from avantifellows/feat/mark-for-reviw
Browse files Browse the repository at this point in the history
Adds Mark For Review button
  • Loading branch information
suryabulusu authored Sep 21, 2024
2 parents 8ae4c9f + 4054078 commit 5b18ab2
Show file tree
Hide file tree
Showing 17 changed files with 303 additions and 25 deletions.
23 changes: 22 additions & 1 deletion jest.init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: "<img />",
};

config.global.stubs = {
InlineSvg: InlineSvg,
InlineSvg,
};
config.global.plugins = [VueClickAway];
47 changes: 37 additions & 10 deletions src/components/InstructionPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -54,27 +54,48 @@
<h4 class="text-lg font-bold m-6">General Instructions</h4>
<div class="ml-11 mr-4">
<ol class="text-justify">
<li>The countdown timer in the top right corner of screen will display the remaining time available for you to complete the test. When the timer reaches zero, the test will end by itself. You will not be required to end or submit your test.</li>
<li>You can click on the <span class="inline-flex items-baseline"><BaseIcon name ='hamburger' class="place-self-center w-4 h-4"></BaseIcon></span> button on the top left corner of the page to expand the Question Palette.</li>
<li>The Question Palette will show the status of each question using one of the following symbols:
<li>
The countdown timer in the top right corner of the screen will display the remaining time available for you to complete the test. When the timer reaches zero, the test will end by itself. You will not be required to end or submit your test.
</li>
<li>
You can click on the
<span class="inline-flex items-baseline">
<BaseIcon name='hamburger' class="place-self-center w-4 h-4 border border-gray-300 rounded bg-gray-100"></BaseIcon>
</span>
button on the top left corner of the page to expand the Question Palette.
</li>
<li>
The Question Palette will show the status of each question using one of the following symbols:
<div class="flex flex-wrap mx-2 md:mx-4 my-2">
<div class="flex items-center my-2 md:mx-4">
<Success></Success>
<Success class="mr-2"></Success>
<span class="ml-6 mr-6">You have answered the question</span>
</div>
<div class="flex items-center my-2 md:mx-4">
<Error></Error>
<Neutral class="mr-2"></Neutral>
<span class="ml-6 mr-6">You have not visited the question yet</span>
</div>
<div class="flex items-center my-2 md:mx-4">
<Neutral></Neutral>
<Error class="mr-2"></Error>
<span class="ml-6 mr-6">You have not answered the question</span>
</div>
<div class="relative flex items-center border border-violet-600 p-2 rounded-md my-2 md:mx-4 w-full">
<Review class="mr-2"></Review>
<span class="mr-2">You have marked the question for review</span>
<span class="bg-violet-800 text-white text-xs font-bold py-0.5 px-2 rounded-full">NEW</span>
</div>
</div>
</li>
<li>You can click on the <span class="inline-flex items-baseline"><BaseIcon name ='hamburger' class="place-self-center w-4 h-4"></BaseIcon></span> button again to collapse the Question Palette.</li>
<li>
You can click on the
<span class="inline-flex items-baseline">
<BaseIcon name='hamburger' class="place-self-center w-4 h-4"></BaseIcon>
</span>
button again to collapse the Question Palette.
</li>
</ol>
</div>

<!-- Answering a question -->
<h4 class="text-lg font-bold m-6">Answering a Question:</h4>
<div class="ml-11 mr-4">
Expand All @@ -84,15 +105,19 @@
<li>To select you answer, click on the button of one of the options.</li>
<li>To deselect your chosen answer, click on the button of the chosen option again or click on the <b>Clear</b> button.</li>
<li>To change your chosen answer, click on the button of another option.</li>
<li>To save your answer, you <b>MUST</b> click on the Save & Next button.</li>
<li>To save your answer, you <b>MUST</b> click on the "Save & Next" button.</li>
<li class="relative border border-violet-600 p-2 rounded-md">
To review a question later, you can click on the "Review >" button. This action <b>will not</b> save your answer.
<span class="bg-violet-800 text-white text-xs font-bold py-0.5 px-2 rounded-full">NEW</span>
</li>
</ol>
</li>
<li>To change your answer to a question that has already been answered, first select that question for answering and then follow the procedure for answering that type of question.</li>
</ol>
</div>
<div class="mt-5 ml-6 mr-4 flex border-red-400 border-1 p-2">
<div class="float-left text-red-400 pr-5 pl-3 text-xl font-bold">!</div>
<div class="float-right text-justify pr-2">Note that selecting an option does NOT save your answer to the current question. Click on <b>Save & Next to save your answer</b> for the current question and then go to the next question.</div>
<div class="float-right text-justify pr-2">Note that selecting an option DOES NOT save your answer to the current question. Click on <b>"Save & Next" to save your answer</b> for the current question and then go to the next question.</div>
</div>
</div>
</template>
Expand All @@ -103,14 +128,16 @@ import BaseIcon from "./UI/Icons/BaseIcon.vue";
import Success from "./Questions/Palette/Success.vue";
import Error from "./Questions/Palette/Error.vue";
import Neutral from "./Questions/Palette/Neutral.vue";
import Review from "./Questions/Palette/Review.vue";
import { quizTitleType, testFormat, questionSetPalette, TimeLimit } from "../types";
export default defineComponent({
name: "InstructionPage",
components: {
BaseIcon,
Success,
Error,
Neutral
Neutral,
Review
},
props: {
title: {
Expand Down
4 changes: 3 additions & 1 deletion src/components/Omr/OmrModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,9 @@ export default defineComponent({
_id: response._id,
question_id: response.question_id,
answer: response.answer,
visited: response.visited
visited: response.visited,
time_spent: response.time_spent,
marked_for_review: response.marked_for_review
}
)
})
Expand Down
17 changes: 17 additions & 0 deletions src/components/Questions/Body.vue
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,15 @@
</div>
</div>
</div>
<!-- labels -->
<div
v-if="isQuizAssessment && !hasQuizEnded && !isSessionAnswerRequestProcessing"
class="mx-6 md:mx-10 pb-4"
>
<span v-if="isMarkedForReview" class="bg-violet-500 text-white text-xs font-bold py-0.5 px-2 rounded-full mr-1">MARKED FOR REVIEW</span>
<span v-if="isAnswerSubmitted" class="bg-emerald-600 text-white text-xs font-bold py-0.5 px-2 rounded-full mr-1">ANSWERED</span>
<span v-if="!isAnswerSubmitted" class="bg-red-500 text-white text-xs font-bold py-0.5 px-2 rounded-full mr-1">NOT ANSWERED</span>
</div>
<!-- Solution container -->
<div
v-if="
Expand Down Expand Up @@ -354,6 +363,14 @@ export default defineComponent({
default: false,
type: Boolean,
},
isMarkedForReview: {
default: false,
type: Boolean,
},
isSessionAnswerRequestProcessing: {
default: false,
type: Boolean,
},
questionType: {
default: questionType.SINGLE_CHOICE,
type: String as PropType<questionType>,
Expand Down
62 changes: 60 additions & 2 deletions src/components/Questions/Footer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,18 @@
@click="clearAnswer"
data-test="clearButton"
></icon-button>

<!-- mark for review button - assessment -->
<icon-button
:class="{
hidden: isOmrMode
}"
:titleConfig="markForReviewButtonTitleConfig"
:buttonClass="markForReviewButtonClass"
:isDisabled="isSessionAnswerRequestProcessing || isMarkedForReview"
@click="toggleMarkForReview"
data-test="markForReviewButton"
></icon-button>
</div>

<div class="place-self-end flex h-full">
Expand Down Expand Up @@ -95,6 +107,8 @@ import {
toRefs,
computed,
PropType,
onMounted,
onBeforeUnmount
} from "vue";
export default defineComponent({
Expand All @@ -108,6 +122,10 @@ export default defineComponent({
default: false,
type: Boolean,
},
isMarkedForReview: {
default: false,
type: Boolean,
},
isPreviousButtonShown: {
default: false,
type: Boolean,
Expand Down Expand Up @@ -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: [
{
Expand All @@ -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":
Expand Down Expand Up @@ -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",
Expand All @@ -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"],
Expand All @@ -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");
}
Expand Down Expand Up @@ -262,26 +307,39 @@ 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,
submitButtonClass,
isQuizAssessment,
};
},
emits: ["submit", "previous", "continue", "clear"],
emits: ["submit", "previous", "continue", "clear", "mark-for-review"],
});
</script>
7 changes: 7 additions & 0 deletions src/components/Questions/Palette/Item.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@
:hasQuizEnded="hasQuizEnded"
data-test="neutral"
></Neutral>
<Review
v-else-if="state == 'review'"
:hasQuizEnded="hasQuizEnded"
data-test="review"
></Review>
<p
class="mt-2 bg-gray-200 border-gray-500 border-1 rounded-md px-2 text-xs"
:class="{
Expand All @@ -39,6 +44,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 { questionState } from "@/types";
export default defineComponent({
Expand All @@ -48,6 +54,7 @@ export default defineComponent({
PartialSuccess,
Error,
Neutral,
Review
},
props: {
state: {
Expand Down
8 changes: 8 additions & 0 deletions src/components/Questions/Palette/QuestionPalette.vue
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@
:title="legendNeutralText"
:hasQuizEnded="hasQuizEnded"
></Neutral>
<div v-if="!hasQuizEnded">
<Review
title="Marked For Review"
:hasQuizEnded="hasQuizEnded"
></Review>
</div>
<div v-if="hasQuizEnded">
<PartialSuccess :title="legendPartialSuccessText" :hasQuizEnded="hasQuizEnded"></PartialSuccess>
</div>
Expand Down Expand Up @@ -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";
Expand All @@ -89,6 +96,7 @@ export default defineComponent({
PartialSuccess,
Error,
Neutral,
Review,
PaletteItem,
InstructionPage
},
Expand Down
Loading

0 comments on commit 5b18ab2

Please sign in to comment.