From 0da45505d7b5bc4d9b1e4aa1e9489f8b1f165725 Mon Sep 17 00:00:00 2001
From: Hassan Abouelela <47495861+HassanAbouelela@users.noreply.github.com>
Date: Mon, 4 Jan 2021 04:00:10 +0300
Subject: Adds Question Rendering

Adds a question component, and calls it on form page. Adds styling for
input types and form page. Lays foundation for validation and
 submission.

Signed-off-by: Hassan Abouelela <47495861+HassanAbouelela@users.noreply.github.com>
---
 src/components/Question.tsx | 128 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 128 insertions(+)
 create mode 100644 src/components/Question.tsx

(limited to 'src/components/Question.tsx')

diff --git a/src/components/Question.tsx b/src/components/Question.tsx
new file mode 100644
index 0000000..1c0fb31
--- /dev/null
+++ b/src/components/Question.tsx
@@ -0,0 +1,128 @@
+/** @jsx jsx */
+import { jsx } from "@emotion/react";
+import React, { ChangeEvent } from "react";
+
+import { Question, QuestionType } from "../api/question";
+import create_input from "./InputTypes";
+
+const _skip_normal_state: Array<QuestionType> = [
+    QuestionType.Radio,
+    QuestionType.Checkbox,
+    QuestionType.Select,
+    QuestionType.Section,
+    QuestionType.Range
+];
+
+export type QuestionProp = {
+    question: Question,
+    public_state: Map<string, string | boolean | null>,
+}
+
+class RenderedQuestion extends React.Component<QuestionProp> {
+    constructor(props: QuestionProp) {
+        super(props);
+        if (props.question.type === QuestionType.TextArea) {
+            this.handler = this.text_area_handler.bind(this);
+        } else {
+            this.handler = this.normal_handler.bind(this);
+        }
+
+        if (!_skip_normal_state.includes(props.question.type)) {
+            this._setState("value", "");
+        }
+    }
+
+    _setState(target: string, value: string | boolean | null, callback?:() => void): void {
+        this.setState({[target]: value}, callback);
+        this.props.public_state.set(target, value);
+    }
+
+    handler(_: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>): void {} // eslint-disable-line
+
+    normal_handler(event: ChangeEvent<HTMLInputElement>): void {
+        let target: string;
+        let value: string | boolean;
+
+        switch (event.target.type) {
+        case QuestionType.Checkbox:
+            target = this.props.question.id;
+            value = event.target.checked;
+            break;
+
+        case QuestionType.Radio:
+            target = "value";
+            if (event.target.parentElement) {
+                value = event.target.parentElement.innerText.trimEnd();
+            } else {
+                value = event.target.value;
+            }
+            break;
+
+        case QuestionType.Select:
+            // Handled by component
+            return;
+
+        default:
+            target = "value";
+            value = event.target.value;
+        }
+
+        this._setState(target, value);
+
+        // Toggle checkbox class
+        if (event.target.type == "checkbox" && event.target.parentElement !== null) {
+            event.target.parentElement.classList.toggle("unselected_checkbox_label");
+            event.target.parentElement.classList.toggle("selected_checkbox_label");
+        }
+    }
+
+    text_area_handler(event: ChangeEvent<HTMLTextAreaElement>): void {
+        this._setState("value", event.target.value);
+    }
+
+    componentDidMount(): void {
+        // Initialize defaults for complex and nested fields
+        const options: string | string[] = this.props.question.data["options"];
+
+        if (this.props.public_state.size === 0) {
+            switch (this.props.question.type) {
+            case QuestionType.Checkbox:
+                if (typeof options === "string") {
+                    return;
+                }
+
+                options.forEach((option, index) => {
+                    this._setState(`${("000" + index).slice(-4)}. ${option}`, false);
+                });
+                break;
+
+            case QuestionType.Range:
+            case QuestionType.Radio:
+            case QuestionType.Select:
+                this._setState("value", null);
+                break;
+            }
+        }
+    }
+
+    render(): JSX.Element {
+        const question = this.props.question;
+
+        if (question.type === QuestionType.Section) {
+            return <div>
+                <h1 className="selectable">{question.name}</h1>
+                { question.data["text"] ? <h3 className="selectable">{question.data["text"]}</h3> : "" }
+                <hr className="section_header"/>
+            </div>;
+        } else {
+            return <div>
+                <h2 className="selectable">
+                    {question.name}<span id={question.required ? "required" : ""} className="required_star">*</span>
+                </h2>
+                { create_input(this.props, this.handler) }<hr/>
+            </div>;
+        }
+    }
+}
+
+export default RenderedQuestion;
-- 
cgit v1.2.3

