Code snippet, better UX in questionnaire

This commit is contained in:
Rotimi Best
2021-07-03 19:29:41 +00:00
parent b76d1c9647
commit fbdfd3321b
17 changed files with 348 additions and 92 deletions
@@ -2,9 +2,9 @@
import {
questionnaire,
handleAddOption,
handleAddQuestion,
handleRemoveOption,
handleRemoveQuestion,
handleCode,
} from "./store/index";
import Delete24 from "carbon-icons-svelte/lib/Delete24";
import AddFilled24 from "carbon-icons-svelte/lib/AddFilled24";
@@ -14,9 +14,10 @@
import Checkbox from "../../../../Form/Checkbox.svelte";
import RadioItem from "../../../../Form/RadioItem.svelte";
import IconButton from "../../../../IconButton/index.svelte";
import Select from "../../../../Form/Select.svelte";
import PrimaryButton from "../../../../PrimaryButton/index.svelte";
import Dropdown from "../../../../Dropdown/index.svelte";
import { VARIANTS } from "../../../../PrimaryButton/constants";
import Select from "../../../../Form/Select.svelte";
import QuestionContainer from "../../../../QuestionContainer/index.svelte";
import {
QUESTION_TYPE,
@@ -26,39 +27,83 @@
// import readme from "../../readme.js";
// let value = readme;
const extraActions = ["Code Snippets", "Upload image"];
const initialQuestionsLength = $questionnaire.questions.length;
function shouldScrollToLast(index, questions) {
const currentQuestionsLength = questions.length;
const isLast = index + 1 === currentQuestionsLength;
return isLast && initialQuestionsLength !== currentQuestionsLength;
}
function handleOptionClick(questionIndex) {
return (actionIndex) => {
switch (actionIndex) {
case 0:
handleCode(questionIndex, true);
break;
case 1:
// uploadImage(questionIndex);
break;
default:
console.log("No option");
}
};
}
</script>
<div class="root relative">
<div class="head flex bg-white items-center justify-between sticky right-0">
<h3>Home task</h3>
<PrimaryButton
variant={VARIANTS.OUTLINED}
onClick={handleAddQuestion}
label="Add Question"
mnb
v
/>
</div>
<div class="w-auto m-auto">
<QuestionContainer isTitle={true}>
<TextField placeholder="Title" bind:value={$questionnaire.title} />
<TextField
placeholder="Title"
bind:value={$questionnaire.title}
className="mb-2"
/>
<TextArea
placeholder="Description and Rules"
bind:value={$questionnaire.description}
/>
</QuestionContainer>
{#each $questionnaire.questions as question}
<QuestionContainer onClose={handleRemoveQuestion(question.id)}>
<div class="flex justify-between">
{#each $questionnaire.questions as question, index}
<QuestionContainer
onClose={handleRemoveQuestion(question.id)}
scrollToQuestion={shouldScrollToLast(index, $questionnaire.questions)}
>
<div class="flex justify-between items-center">
<div class="mr-2 w-3/5">
<TextField placeholder="Question" bind:value={question.title} />
</div>
<Dropdown
options={extraActions}
handleOptionClick={handleOptionClick(question.id)}
>
<AddFilled24 class="carbon-icon" />
<p class="ml-2 text-gray-600">Add</p>
</Dropdown>
<Select bind:value={question.type} options={QUESTION_TYPES} />
</div>
<div class="flex flex-col">
{#if typeof question.code === "string"}
<div class="flex justify-between items-center my-3 w-3/5">
<TextArea
bind:value={question.code}
rows="2"
placeholder="Write your code"
/>
<IconButton
value="write-code"
onClick={() => handleCode(question.id, false)}
>
<Delete24 class="carbon-icon" />
</IconButton>
</div>
{/if}
<div class="flex flex-col mt-2">
{#each question.options as option}
{#if QUESTION_TYPE.RADIO === question.type}
<RadioItem
@@ -86,7 +131,7 @@
<div slot="iconbutton">
<IconButton
value={option.id}
onClick={handleRemoveOption(question.id)}
onClick={handleRemoveOption(question.id, option.id)}
>
<Delete24 class="carbon-icon" />
</IconButton>
@@ -101,24 +146,18 @@
</div>
{#if QUESTION_TYPE.TEXTAREA !== question.type}
<div class="flex items-center">
<IconButton onClick={handleAddOption(question.id)}>
<div class="flex items-center mt-3">
<PrimaryButton
disablePadding={true}
className="p-2"
variant={VARIANTS.OUTLINED}
onClick={handleAddOption(question.id)}
>
<AddFilled24 class="carbon-icon" />
</IconButton>
<p class="italic">Add option</p>
<p class="ml-2">Add option</p>
</PrimaryButton>
</div>
{/if}
</QuestionContainer>
{/each}
</div>
<style>
.root {
max-width: 700px;
}
.head {
top: 63px;
z-index: 2;
}
</style>
@@ -1,9 +1,14 @@
<script>
import { questionnaire } from "./store/index";
// import RadioQuestion from "../../../../Question/RadioQuestion/index.svelte";
import RadioQuestion from "../../../../Question/RadioQuestion/index.svelte";
import CheckboxQuestion from "../../../../Question/CheckboxQuestion/index.svelte";
import PrimaryButton from "../../../../PrimaryButton/index.svelte";
import {
QUESTION_TYPE,
QUESTION_TYPES,
} from "../../../../Question/constants";
import Checkbox from "../../../../Form/Checkbox.svelte";
let answers = {};
let currentQuestionIndex = 0;
let currentQuestion = {};
@@ -34,7 +39,10 @@
currentQuestionIndex += 1;
}
$: currentQuestion = questions[currentQuestionIndex - 1];
$: {
currentQuestion = questions[currentQuestionIndex - 1];
console.log("currentQuestion", currentQuestion);
}
function onPrevious() {
currentQuestionIndex -= 1;
@@ -43,7 +51,7 @@
{#if currentQuestionIndex === 0}
<div>
<h2>{title}</h2>
<h2 class="my-1">{title}</h2>
<p>{description}</p>
<PrimaryButton
@@ -57,21 +65,24 @@
<pre>
<code>{JSON.stringify(answers)}</code>
</pre>
{:else}
<!-- <RadioQuestion
{:else if QUESTION_TYPE.RADIO === currentQuestion.type}
<RadioQuestion
title={currentQuestionIndex + ". " + currentQuestion.title}
name={currentQuestion.name}
name={`${currentQuestion.id}`}
options={currentQuestion.options}
defaultValue={answers[currentQuestion.name] || false}
code={currentQuestion.code}
defaultValue={answers[`${currentQuestion.id}`] || false}
{onSubmit}
{onPrevious}
disablePreviousButton={currentQuestionIndex === 1}
isLast={currentQuestionIndex === questions.length}
/> -->
/>
{:else if QUESTION_TYPE.CHECKBOX === currentQuestion.type}
<CheckboxQuestion
title={currentQuestionIndex + ". " + currentQuestion.title}
name={`${currentQuestion.id}`}
options={currentQuestion.options}
code={currentQuestion.code}
defaultValue={answers[currentQuestion.id] || []}
onSubmit={onCheckboxSubmit}
{onPrevious}
@@ -1,25 +1,48 @@
<script>
import PrimaryContainedButton from "../../../../PrimaryContainedButton/index.svelte";
import { handleAddQuestion } from "./store/index";
import PrimaryButton from "../../../../PrimaryButton/index.svelte";
import PageBody from "../../../../PageBody/index.svelte";
import EditMode from "./EditMode.svelte";
import ViewMode from "./ViewMode.svelte";
import MODES from "../../../../../utils/constants/mode.js";
import { VARIANTS } from "../../../../PrimaryButton/constants";
let mode = MODES.view;
let preview = false;
</script>
<PageBody>
<svelte:fragment slot="header">
<h3>Lesson 2 home exercises</h3>
<PrimaryContainedButton
label={mode === MODES.edit ? "Save" : "Edit"}
handleClick={() => (mode = mode === MODES.edit ? MODES.view : MODES.edit)}
/>
<div class="flex items-center">
<PrimaryButton
className="mr-2"
variant={VARIANTS.OUTLINED}
label={mode === MODES.edit ? "Save" : "Edit"}
onClick={() => (mode = mode === MODES.edit ? MODES.view : MODES.edit)}
/>
{#if mode === MODES.edit}
<PrimaryButton
variant={VARIANTS.OUTLINED}
label={preview ? "Edit" : "Preview"}
onClick={() => (preview = !preview)}
/>
{/if}
</div>
{#if mode === MODES.edit}
<div class="flex items-center">
<PrimaryButton
variant={VARIANTS.OUTLINED}
onClick={handleAddQuestion}
label="Add Question"
/>
</div>
{/if}
</svelte:fragment>
{#if mode === MODES.edit}
<EditMode />
{:else}
<ViewMode />
<ViewMode {preview} />
{/if}
</PageBody>
@@ -11,19 +11,23 @@ export const questionnaire = writable({
questions: [
{
id: "react-founder",
type: QUESTION_TYPE.CHECKBOX,
type: QUESTION_TYPE.RADIO,
title: "Who is the creator of React.js",
options: [
{
id: 1,
value: "Dan Abrahmov",
},
{
id: 2,
value: "Google",
},
{
id: 3,
value: "Facebook",
},
{
id: 4,
value: "Traversy Media",
},
],
@@ -32,17 +36,26 @@ export const questionnaire = writable({
id: "vue-founder",
type: QUESTION_TYPE.CHECKBOX,
title: "Who is the creator of Vue.js",
code: `const name = 'Josh Perez';\nconst element = <h1>Hello, {name}</h1>;\n\nReactDOM.render(\n
element,\,
document.getElementById('root')
)
`,
options: [
{
id: 1,
value: "Evan Vue",
},
{
id: 2,
value: "Mark Zukerberg",
},
{
id: 3,
value: "Prince Charles",
},
{
id: 4,
value: "Bill Gates",
},
],
@@ -53,34 +66,28 @@ export const questionnaire = writable({
title: "Who is the creator of Svelte.js",
options: [
{
id: 1,
value: "Hillary Svelte",
},
{
id: 2,
value: "Mircosoft",
},
{
id: 3,
value: "Elevate",
},
{
id: 4,
value: "Coding Train",
},
],
},
{
id: "angular-founder",
type: QUESTION_TYPE.CHECKBOX,
type: QUESTION_TYPE.TEXTAREA,
title: "Who is the creator of Angular.js",
options: [
{
value: "Laryy page",
},
{
value: "Google",
},
{
value: "Sales Force",
},
],
value: "",
},
],
});
@@ -95,6 +102,7 @@ export function handleAddQuestion() {
{
...QUESTION_TEMPLATE,
id: questions.length + 1,
value: "",
options: [
{
id: 1,
@@ -120,7 +128,7 @@ export function handleAddOption(questionId) {
options: [
...question.options,
{
id: question.options.length + 1,
id: question.options[question.options.length - 1].id + 1,
value: "",
},
],
@@ -143,6 +151,11 @@ export function handleRemoveOption(questionId, optionId) {
...q,
questions: questions.map((question) => {
if (question.id === questionId) {
console.log(
"filter",
optionId,
question.options.filter((option) => option.id !== optionId)
);
return {
...question,
options: [
@@ -170,3 +183,23 @@ export function handleRemoveQuestion(questionId) {
});
};
}
export function handleCode(questionId, shouldAdd = true) {
questionnaire.update((q) => {
const { questions } = q;
return {
...q,
questions: questions.map((question) => {
if (question.id === questionId) {
return {
...question,
code: shouldAdd ? "" : undefined,
};
}
return question;
}),
};
});
}