Skip to content

Commit

Permalink
Merge pull request #132 from avantifellows/instruction_button
Browse files Browse the repository at this point in the history
Adding a Instructions Button in the QuestionPalette to show InstructionPage
  • Loading branch information
suryabulusu authored Oct 17, 2023
2 parents 7c1df64 + a85a596 commit c412973
Show file tree
Hide file tree
Showing 9 changed files with 308 additions and 114 deletions.
32 changes: 16 additions & 16 deletions src/components/InstructionPage.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div class="xl:mr-20 xl:ml-10 lg:mr-24 lg:ml-24 md:mr-48 md:ml-48 sm:mr-60 sm:ml-60">
<h4 class="text-lg font-bold m-6 ">Test Paper Overview</h4>
<div>
<h4 class="text-lg font-bold m-6">Test Paper Overview</h4>
<!-- Table -->
<table class="table-auto m-4">
<!-- row 1 -->
Expand All @@ -11,12 +11,12 @@
<!-- row 2 -->
<tr>
<th class="border-black border-1 text-left px-4 py-2">Test Format</th>
<td class="border-black border-1 px-4 py-2" data-test="test-format">{{ testFormatMapping.get($props.testFormat) }} </td>
<td class="border-black border-1 px-4 py-2" data-test="test-format">{{ testFormatMapping.get($props.testFormat || "") }} </td>
</tr>
<!-- row 3 -->
<tr>
<th class="border-black border-1 text-left px-4 py-2">Duration</th>
<td class="border-black border-1 px-4 py-2" data-test="quiz-time-limit">{{ ($props.quizTimeLimit)/60 }} minutes</td>
<td class="border-black border-1 px-4 py-2" data-test="quiz-time-limit">{{ ($props.quizTimeLimit?.max || 0)/60 }} minutes</td>
</tr>
<!-- row 4 -->
<tr>
Expand Down Expand Up @@ -45,12 +45,12 @@
</p>
<!-- iterating over every questionset and printing title and its description -->
<div
v-for="(questionSet, index) in questionSets" :key="index">
<li class="text-base mt-2 ml-7 font-semibold leading-none mr-4" :data-test="`questionSetTitle-${index}`">{{ questionSet.title }}</li>
v-for="(questionSetState, index) in questionSetStates" :key="index">
<li class="text-base mt-2 ml-7 font-semibold leading-none mr-4" :data-test="`questionSetTitle-${index}`">{{ questionSetState.title }}</li>
<div class="ml-12 mr-4 mt-1" :data-test="`no-of-questions-${index}`">
There are {{ questionSet.questions.length }} questions, out of which only {{ questionSet.max_questions_allowed_to_attempt }} questions need to be attempted.
There are {{ questionSetState.paletteItems.length }} questions, out of which only {{ questionSetState.maxQuestionsAllowedToAttempt }} questions need to be attempted.
</div>
<div class="text-base mx-2 mb-4 leading-tight text-slate-500 ml-12 mr-4" :data-test="`questionSetInstruction-${index}`" v-html="questionSet.description"></div>
<div class="text-base mx-2 mb-4 leading-tight text-slate-500 ml-12 mr-4" :data-test="`questionSetInstruction-${index}`" v-html="questionSetState.instructionPageText"></div>
</div>
</div>
<!-- general Instruction -->
Expand Down Expand Up @@ -106,7 +106,7 @@ 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 { quizTitleType, testFormat, QuestionSet } from "../types";
import { quizTitleType, testFormat, questionSetPalette, TimeLimit } from "../types";
export default defineComponent({
name: "InstructionPage",
components: {
Expand All @@ -133,24 +133,24 @@ export default defineComponent({
required: true
},
quizTimeLimit: {
type: Number,
required: true
type: Object as PropType<TimeLimit> || null,
default: null
},
questionSets: {
required: true,
type: Array as PropType<QuestionSet[]>
questionSetStates: {
type: Array as PropType<questionSetPalette[]>,
default: () => []
},
testFormat: {
type: [null, String] as PropType<testFormat>,
required: true
default: null
},
},
setup(props) {
const isTestFST = computed(() => props.testFormat == "full_syllabus_test")

// to extract the questionSetTitles from questionSets (eg. Physics - Section A)
const questionSetTitles = computed(() => {
return props.questionSets.map(questionSet => questionSet.title);
return props.questionSetStates.map(questionSetState => questionSetState.title);
});

// to split the questionSetTitles from char "-" (eg. Physics)
Expand Down
34 changes: 32 additions & 2 deletions src/components/Questions/Body.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@
:hasQuizEnded="hasQuizEnded"
:questionSetStates="questionSetStates"
:currentQuestionIndex="currentQuestionIndex"
:title="title"
:subject="subject"
:testFormat="testFormat"
:maxMarks="maxMarks"
:numQuestions="numQuestions"
:quizTimeLimit="quizTimeLimit"
class="absolute w-full h-full sm:w-2/3 lg:w-1/2 xl:w-1/3 z-10"
@navigate="navigateToQuestion"
data-test="questionPalette"
Expand Down Expand Up @@ -160,7 +166,7 @@ import {
onUpdated
} from "vue"
import BaseIcon from "../UI/Icons/BaseIcon.vue"
import { quizType, questionSetPalette, questionType, questionTypeHeaderText } from "../../types"
import { quizType, questionSetPalette, questionType, questionTypeHeaderText, quizTitleType, testFormat, TimeLimit } from "../../types"
import QuestionPalette from "./Palette/QuestionPalette.vue"
const MAX_LENGTH_NUMERICAL_CHARACTERS: number = 10 // max length of characters in numerical answer textbox
Expand Down Expand Up @@ -256,7 +262,31 @@ export default defineComponent({
questionSetTitle: {
type: String,
default: ""
}
},
title: {
type: [null, String] as PropType<quizTitleType>,
required: true,
},
subject: {
type: String,
required: true,
},
numQuestions: {
type: Number,
required: true,
},
maxMarks: {
type: Number,
required: true
},
quizTimeLimit: {
type: Object as PropType<TimeLimit> || null,
default: null
},
testFormat: {
type: [null, String] as PropType<testFormat>,
default: null
},
},
setup(props, context) {
const isQuizAssessment = computed(() => props.quizType == "assessment")
Expand Down
178 changes: 137 additions & 41 deletions src/components/Questions/Palette/QuestionPalette.vue
Original file line number Diff line number Diff line change
@@ -1,46 +1,74 @@
<template>
<div class="bg-white p-4 sm:p-6 lg:p-8 overflow-auto sm:w-1/3 lg:w-1/3 xl:w-1/3">
<div
class="bg-gray-200 rounded-md p-4 grid grid-rows-2 space-y-2"
>
<div class="grid grid-cols-2">
<Success
:title="legendSuccessText"
:hasQuizEnded="hasQuizEnded"
></Success>
<Error :title="legendErrorText" :hasQuizEnded="hasQuizEnded"></Error>
</div>

<div class="grid grid-cols-2">
<Neutral
:title="legendNeutralText"
:hasQuizEnded="hasQuizEnded"
></Neutral>
<div v-if="hasQuizEnded">
<PartialSuccess :title="legendPartialSuccessText" :hasQuizEnded="hasQuizEnded"></PartialSuccess>
</div>
</div>
<div class="inline-flex rounded-md w-full" role="group">
<button
:class="togglePaletteButtonClass"
class="mr-1"
type="button"
@click="togglePalette"
data-test="togglePalette"
>PALETTE</button>
<button
:class="toggleInstructionsButtonClass"
class="ml-1"
type="button"
@click="toggleInstructions"
data-test="toggleInstructions"
>INSTRUCTIONS</button>
</div>

<div
v-for="(questionSetState, index) in questionSetStates" :key="index" class="space-y-2">
<p :class="titleTextClass" :data-test="`paletteTitle-${index}`">{{ questionSetState.title }}</p>
<div :class="instructionTextClass" :data-test="`paletteInstruction-${index}`" v-html="questionSetState.instructionText"></div>
<div class="grid grid-cols-5 bp-500:grid-cols-6 lg:grid-cols-7 xl:grid-cols-8 mt-4 space-y-4">
<PaletteItem
v-for="(questionState, qindex) in questionSetState.paletteItems"
class="hover:cursor-pointer"
:class="{ 'mt-4': qindex == 0 }"
:key="qindex"
:index="questionState.index"
<InstructionPage
v-if="showInstructionButton"
:title="title"
:subject="subject"
:testFormat="testFormat"
:maxMarks="maxMarks"
:max-questions-allowed-to-attempt="numQuestions"
:quizTimeLimit="quizTimeLimit"
:questionSetStates = "questionSetStates"
data-test="instruction-page"
/>
<div v-if="showPaletteButton" data-test="question-palette">
<div
class="bg-gray-200 rounded-md p-4 grid grid-rows-2 space-y-2 mt-6"
>
<div class="grid grid-cols-2">
<Success
:title="legendSuccessText"
:hasQuizEnded="hasQuizEnded"
:state="questionState.value"
:isHighlighted="currentQuestionIndex == questionState.index"
@click="navigateToQuestion(questionState.index)"
:data-test="`paletteItem-${questionState.index}`"
></PaletteItem>
></Success>
<Error :title="legendErrorText" :hasQuizEnded="hasQuizEnded"></Error>
</div>

<div class="grid grid-cols-2">
<Neutral
:title="legendNeutralText"
:hasQuizEnded="hasQuizEnded"
></Neutral>
<div v-if="hasQuizEnded">
<PartialSuccess :title="legendPartialSuccessText" :hasQuizEnded="hasQuizEnded"></PartialSuccess>
</div>
</div>
</div>

<div
v-for="(questionSetState, index) in questionSetStates" :key="index" class="space-y-2">
<p :class="titleTextClass" :data-test="`paletteTitle-${index}`">{{ questionSetState.title }}</p>
<div :class="instructionTextClass" :data-test="`paletteInstruction-${index}`" v-html="questionSetState.instructionText"></div>
<div class="grid grid-cols-5 bp-500:grid-cols-6 lg:grid-cols-7 xl:grid-cols-8 mt-4 space-y-4">
<PaletteItem
v-for="(questionState, qindex) in questionSetState.paletteItems"
class="hover:cursor-pointer"
:class="{ 'mt-4': qindex == 0 }"
:key="qindex"
:index="questionState.index"
:hasQuizEnded="hasQuizEnded"
:state="questionState.value"
:isHighlighted="currentQuestionIndex == questionState.index"
@click="navigateToQuestion(questionState.index)"
:data-test="`paletteItem-${questionState.index}`"
></PaletteItem>
</div>
</div>
</div>
</div>
</template>
Expand All @@ -51,8 +79,9 @@ import PartialSuccess from "./PartialSuccess.vue";
import Error from "./Error.vue";
import Neutral from "./Neutral.vue";
import PaletteItem from "./Item.vue";
import { questionSetPalette } from "../../../types";
import { defineComponent, computed, PropType } from "vue";
import InstructionPage from "@/components/InstructionPage.vue";
import { TimeLimit, questionSetPalette, quizTitleType, testFormat } from "../../../types";
import { defineComponent, computed, PropType, reactive } from "vue";
export default defineComponent({
components: {
Expand All @@ -61,6 +90,7 @@ export default defineComponent({
Error,
Neutral,
PaletteItem,
InstructionPage
},
props: {
hasQuizEnded: {
Expand All @@ -75,12 +105,49 @@ export default defineComponent({
type: Number,
default: 0,
},
title: {
type: [null, String] as PropType<quizTitleType>,
required: true,
},
subject: {
type: String,
required: true,
},
numQuestions: {
type: Number,
required: true,
},
maxMarks: {
type: Number,
required: true
},
quizTimeLimit: {
type: Object as PropType<TimeLimit> || null,
default: null
},
testFormat: {
type: [null, String] as PropType<testFormat>,
default: null
},
},
setup(props, context) {
function navigateToQuestion(questionIndex: number) {
context.emit("navigate", questionIndex);
}
function toggleInstructions() {
if (state.showInstructions == false) {
state.showInstructions = !state.showInstructions;
state.showPalette = !state.showPalette
}
};
function togglePalette() {
if (state.showPalette == false) {
state.showPalette = !state.showPalette;
state.showInstructions = !state.showInstructions;
}
}
const legendSuccessText = computed(() =>
props.hasQuizEnded ? "Correct" : "Answered"
);
Expand All @@ -93,18 +160,47 @@ export default defineComponent({
const legendPartialSuccessText = "Partially Correct"
const state = {
const showInstructionButton = computed(() => state.showInstructions == true)
const showPaletteButton = computed(() => state.showPalette == true)
const toggleInstructionsButtonClass = computed(() => [
{
"bg-primary": !showInstructionButton.value,
"bg-orange-300 hover:cursor-not-allowed": showInstructionButton.value,
},
`w-1/2 font-bold text-white p-2 px-4 bp-500:p-4 bp-500:px-6 rounded-lg sm:rounded-2xl shadow-xl border shadow-lg ring-primary`,
]);
const togglePaletteButtonClass = computed(() => [
{
"bg-primary": !showPaletteButton.value,
"bg-orange-300 hover:cursor-not-allowed": showPaletteButton.value,
},
`w-1/2 font-bold text-white p-2 px-4 bp-500:p-4 bp-500:px-6 rounded-lg sm:rounded-2xl shadow-xl border shadow-lg ring-primary`,
]);
const state = reactive({
instructionTextClass:
"text-lg md:text-xl lg:text-2xl mx-4 mt-2 leading-none text-slate-500",
titleTextClass:
"text-lg md:text-xl lg:text-2xl mx-4 mt-10 font-bold leading-tight whitespace-pre-wrap",
}
instructionsButtonClass:
"bg-gray-300 w-full font-bold ring-gray-500 p-2 px-4 bp-500:p-4 bp-500:px-6 rounded-lg sm:rounded-2xl shadow-xl",
showInstructions: false,
showPalette: true,
});
return {
...state,
navigateToQuestion,
toggleInstructions,
togglePalette,
legendSuccessText,
legendPartialSuccessText,
showInstructionButton,
showPaletteButton,
toggleInstructionsButtonClass,
togglePaletteButtonClass,
legendErrorText,
legendNeutralText,
};
Expand Down
Loading

0 comments on commit c412973

Please sign in to comment.