aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Hassan Abouelela <[email protected]>2021-01-06 08:00:31 +0300
committerGravatar Hassan Abouelela <[email protected]>2021-01-06 09:36:01 +0300
commit996c14afb9d81e962ef66b99bd869bce4f3688f0 (patch)
treeea19a979c62a0da3685a1ea44a624ee2863320cc
parentFixes Model Casing (diff)
Breaks Up CSS Into Components
Moves the styles from the CSS file, into emotion CSS in each component's file to make navigation easier, and keep CSS and JSX together.Drops raw-loader dependency. Signed-off-by: Hassan Abouelela <[email protected]>
-rw-r--r--package.json1
-rw-r--r--src/colors.ts2
-rw-r--r--src/commonStyles.tsx39
-rw-r--r--src/components/InputTypes/Checkbox.tsx62
-rw-r--r--src/components/InputTypes/Radio.tsx4
-rw-r--r--src/components/InputTypes/Range.tsx101
-rw-r--r--src/components/InputTypes/Select.tsx109
-rw-r--r--src/components/InputTypes/ShortText.tsx3
-rw-r--r--src/components/InputTypes/TextArea.tsx14
-rw-r--r--src/components/Question.tsx61
-rw-r--r--src/components/ScrollToTop.tsx3
-rw-r--r--src/pages/FormPage.tsx168
-rw-r--r--src/pages/css/FormPage.css452
-rw-r--r--src/tests/pages/FormPage.test.tsx2
-rw-r--r--webpack.config.js6
-rw-r--r--yarn.lock8
16 files changed, 498 insertions, 537 deletions
diff --git a/package.json b/package.json
index e74aa57..c3e21ce 100644
--- a/package.json
+++ b/package.json
@@ -22,7 +22,6 @@
"fs-extra": "9.0.1",
"html-webpack-plugin": "4.5.1",
"identity-obj-proxy": "3.0.0",
- "raw-loader": "4.0.2",
"react": "17.0.1",
"react-app-polyfill": "2.0.0",
"react-dom": "17.0.1",
diff --git a/src/colors.ts b/src/colors.ts
index e9c74b1..52b48cb 100644
--- a/src/colors.ts
+++ b/src/colors.ts
@@ -1,8 +1,10 @@
export default {
blurple: "#7289DA",
+ darkerBlurple: "#4E609C",
darkButNotBlack: "#2C2F33",
notQuiteBlack: "#23272A",
greyple: "#99AAB5",
+ darkerGreyple: "#6E7D88",
error: "#f04747",
success: "#43b581"
};
diff --git a/src/commonStyles.tsx b/src/commonStyles.tsx
new file mode 100644
index 0000000..d47dea7
--- /dev/null
+++ b/src/commonStyles.tsx
@@ -0,0 +1,39 @@
+import { css } from "@emotion/react";
+
+const selectable = css`
+ -moz-user-select: text;
+ -webkit-user-select: text;
+ -ms-user-select: text;
+ user-select: text;
+`;
+
+const unselectable = css`
+ -moz-user-select: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+`;
+
+const textInputs = css`
+ display: inline-block;
+ width: min(20rem, 90%);
+ height: 100%;
+ min-height: 2rem;
+
+ background: whitesmoke;
+
+ color: black;
+ padding: 0 1rem;
+ font: inherit;
+
+ margin-bottom: 0;
+
+ border: 0.1rem solid black;
+ border-radius: 8px;
+`;
+
+export {
+ selectable,
+ unselectable,
+ textInputs
+};
diff --git a/src/components/InputTypes/Checkbox.tsx b/src/components/InputTypes/Checkbox.tsx
index ed02b83..07872d6 100644
--- a/src/components/InputTypes/Checkbox.tsx
+++ b/src/components/InputTypes/Checkbox.tsx
@@ -1,6 +1,7 @@
/** @jsx jsx */
-import { jsx } from "@emotion/react";
+import { jsx, css } from "@emotion/react";
import React, { ChangeEvent } from "react";
+import colors from "../../colors";
interface CheckboxProps {
index: number,
@@ -8,13 +9,66 @@ interface CheckboxProps {
handler: (event: ChangeEvent<HTMLInputElement>) => void
}
+const generalStyles = css`
+ label {
+ display: inline-block;
+ position: relative;
+ top: 0.25em;
+
+ width: 1em;
+ height: 1em;
+
+ margin: 1rem 0.5rem 0 0;
+ border: whitesmoke 0.2rem solid;
+ border-radius: 25%;
+
+ transition: background-color 300ms;
+ }
+
+ .unselected {
+ background-color: white;
+ }
+
+ .unselected:hover {
+ background-color: lightgray;
+ }
+
+ input {
+ position: absolute;
+ opacity: 0;
+ height: 0;
+ width: 0;
+ }
+
+ .checkmark {
+ position: absolute;
+ }
+`;
+
+const activeStyles = css`
+ .selected {
+ background-color: ${colors.blurple};
+ }
+
+ .selected .checkmark {
+ width: 0.30rem;
+ height: 0.60rem;
+ left: 0.25em;
+
+ border: solid white;
+ border-width: 0 0.2rem 0.2rem 0;
+
+ transform: rotate(45deg);
+ }
+`;
+
export default function Checkbox(props: CheckboxProps): JSX.Element {
return (
- <label>
- <label className="unselected_checkbox_label checkbox_label unselectable">
+ <label css={[generalStyles, activeStyles]}>
+ <label className="unselected">
<input type="checkbox" value={props.option}
name={`${("000" + props.index).slice(-4)}. ${props.option}`} onChange={props.handler}/>
- <span className="checkmark_span"/>
+ <span className="checkmark"/>
</label>
{props.option}<br/>
</label>
diff --git a/src/components/InputTypes/Radio.tsx b/src/components/InputTypes/Radio.tsx
index 81f8375..be0f60e 100644
--- a/src/components/InputTypes/Radio.tsx
+++ b/src/components/InputTypes/Radio.tsx
@@ -1,5 +1,5 @@
/** @jsx jsx */
-import { jsx } from "@emotion/react";
+import { jsx, css } from "@emotion/react";
import React, { ChangeEvent } from "react";
interface RadioProps {
@@ -11,7 +11,7 @@ interface RadioProps {
export default function Radio(props: RadioProps): JSX.Element {
return (
<label>
- <input type="radio" name={props.question_id} className="radio" onChange={props.handler}/>
+ <input type="radio" name={props.question_id} css={css`margin: 1rem 0.5rem 0 0;`} onChange={props.handler}/>
{props.option}<br/>
</label>
);
diff --git a/src/components/InputTypes/Range.tsx b/src/components/InputTypes/Range.tsx
index a0260ad..af46b05 100644
--- a/src/components/InputTypes/Range.tsx
+++ b/src/components/InputTypes/Range.tsx
@@ -1,6 +1,7 @@
/** @jsx jsx */
-import { jsx } from "@emotion/react";
+import { jsx, css } from "@emotion/react";
import React from "react";
+import colors from "../../colors";
interface RangeProps {
question_id: string,
@@ -8,13 +9,12 @@ interface RangeProps {
state_dict: Map<string, string | boolean | null>
}
-let last_selection: Element;
-
interface handler_props {
state_dict: Map<string, string | boolean | null>,
ref: React.RefObject<HTMLLabelElement>
}
+let last_selection: Element;
function handler(this: handler_props): void {
if (last_selection) {
last_selection.classList.toggle("selected");
@@ -29,23 +29,108 @@ function handler(this: handler_props): void {
this.state_dict.set("value", value);
}
+const containerStyles = css`
+ display: flex;
+ justify-content: space-between;
+ position: relative;
+ width: 100%;
+
+ @media (max-width: 800px) {
+ width: 20%;
+ display: block;
+ margin: 0 auto;
+
+ label span {
+ margin-left: 0;
+ transform: translateY(1.6rem) translateX(2rem);
+ }
+ }
+`;
+
+const optionStyles = css`
+ display: inline-block;
+ transform: translateX(-50%);
+ margin: 0 50%;
+
+ white-space: nowrap;
+
+ transition: transform 300ms;
+`;
+
+const rangeDotStyles = css`
+ .range_dot {
+ width: 0.8rem;
+ height: 0.8rem;
+ background-color: whitesmoke;
+
+ border: 0.2rem solid whitesmoke;
+ border-radius: 50%;
+
+ transition: background-color 300ms;
+ }
+
+ .range_dot.selected {
+ background-color: ${colors.blurple};
+ }
+
+ @media (max-width: 800px) {
+ .range_dot {
+ margin-bottom: 1.5rem;
+ }
+ }
+`;
+
+const sliderContainerStyles = css`
+ display: flex;
+ justify-content: center;
+ width: 100%;
+
+ position: absolute;
+ z-index: -1;
+
+ top: 2rem;
+
+ transition: all 300ms;
+
+ @media (max-width: 800px) {
+ width: 0.5rem;
+ height: 88%;
+
+ left: 0.32rem;
+
+ background: whitesmoke;
+ }
+`;
+
+const sliderStyles = css`
+ width: 98%; /* Needs to be slightly smaller than container to work on all devices */
+ height: 0.5rem;
+ background-color: whitesmoke;
+
+ transition: transform 300ms;
+
+ @media (max-width: 800px) {
+ display: none;
+ }
+`;
+
export default function Range(props: RangeProps): JSX.Element {
const range = props.options.map((option, index) => {
const ref: React.RefObject<HTMLLabelElement> = React.createRef();
return (
- <label key={index} ref={ref} onClick={handler.bind({state_dict: props.state_dict, ref: ref})}>
- <span>{option}</span>
+ <label key={index} ref={ref} css={css`width: 1rem;`} onClick={handler.bind({state_dict: props.state_dict, ref})}>
+ <span css={optionStyles}>{option}</span>
<div className="range_dot"/>
</label>
);
});
return (
- <div className="range">
+ <div css={[containerStyles, rangeDotStyles]}>
{ range }
- <div className="range_slider_container">
- <div className="range_slider"/>
+ <div css={sliderContainerStyles}>
+ <div css={sliderStyles}/>
</div>
</div>
);
diff --git a/src/components/InputTypes/Select.tsx b/src/components/InputTypes/Select.tsx
index 355f5d0..de763bf 100644
--- a/src/components/InputTypes/Select.tsx
+++ b/src/components/InputTypes/Select.tsx
@@ -1,5 +1,5 @@
/** @jsx jsx */
-import { jsx } from "@emotion/react";
+import { jsx, css } from "@emotion/react";
import React from "react";
interface SelectProps {
@@ -12,11 +12,86 @@ interface HandlerProps {
ref: React.RefObject<HTMLDivElement>
}
-class Select extends React.Component<SelectProps> {
- constructor(props: SelectProps) {
- super(props);
- }
+const containerStyles = css`
+ .container {
+ display: inline-block;
+ position: relative;
+
+ width: min(20rem, 90%);
+ height: 100%;
+ min-height: 2rem;
+
+ background: whitesmoke;
+
+ color: black;
+ text-align: center;
+
+ margin-bottom: 0;
+
+ border: 0.1rem solid black;
+ border-radius: 8px;
+
+ transition: border-radius 400ms;
+ }
+
+ .container.active {
+ height: auto;
+ border-radius: 8px 8px 0 0;
+ }
+`;
+
+const arrowStyles = css`
+ .arrow {
+ display: inline-block;
+ height: 0.5rem;
+ width: 0.5rem;
+
+ position: relative;
+ float: right;
+ right: 1em;
+ top: 0.7rem;
+
+ border: solid black;
+ border-width: 0 0.2rem 0.2rem 0;
+ transform: rotate(45deg);
+ transition: transform 400ms;
+ }
+
+ .active .arrow {
+ transform: translateY(40%) rotate(225deg);
+ }
+`;
+
+const optionContainer = css`
+ .option_container {
+ display: block;
+ position: absolute;
+ width: 100%;
+
+ /* Need to account for margin */
+ left: -0.1rem;
+
+ visibility: hidden;
+ opacity: 0;
+
+ background: whitesmoke;
+ overflow: hidden;
+
+ border: 0.1rem solid black;
+ border-radius: 0 0 8px 8px;
+ border-top: none;
+
+ transition: opacity 400ms, visibility 400ms;
+ }
+
+ .active .option_container {
+ visibility: visible;
+ opacity: 1;
+ }
+`;
+
+class Select extends React.Component<SelectProps> {
click_handler(this: HandlerProps, event: React.MouseEvent<HTMLDivElement, MouseEvent>): void {
if (!this.ref.current) {
return;
@@ -43,18 +118,24 @@ class Select extends React.Component<SelectProps> {
render(): JSX.Element {
const container_ref: React.RefObject<HTMLDivElement> = React.createRef();
- return (
- <div className="select_container" ref={container_ref} onClick={this.click_handler.bind({ref: container_ref, props: this.props})}>
- <span className="select_arrow"/>
- <span className="selected_option">...</span>
- <div className="select_options_container">
- <div className="select_options">
- { this.props.options.map((option, index) => <div key={index}><hr/><div id="option">{option}</div></div>) }
- </div>
+ const element: JSX.Element = (
+ <div className="container" ref={container_ref} onClick={this.click_handler.bind({ref: container_ref, props: this.props})}>
+ <span className="arrow"/>
+ <span className="selected_option" css={css`display: block; padding: 0.5rem 0;`}>...</span>
+
+ <div className="option_container">
+ { this.props.options.map((option, index) => (
+ <div css={css`:hover { background-color: lightgray; }`} key={index}>
+ <hr css={css`margin: 0 1rem;`}/>
+ <div id="option" css={css`padding: 0.75rem;`}>{option}</div>
+ </div>
+ )) }
</div>
</div>
);
+
+ return <div css={[containerStyles, arrowStyles, optionContainer]}>{ element }</div>;
}
}
-export default Select; \ No newline at end of file
+export default Select;
diff --git a/src/components/InputTypes/ShortText.tsx b/src/components/InputTypes/ShortText.tsx
index 182571a..1e38bcd 100644
--- a/src/components/InputTypes/ShortText.tsx
+++ b/src/components/InputTypes/ShortText.tsx
@@ -1,11 +1,12 @@
/** @jsx jsx */
import { jsx } from "@emotion/react";
import React, { ChangeEvent } from "react";
+import { textInputs } from "../../commonStyles";
interface ShortTextProps {
handler: (event: ChangeEvent<HTMLInputElement>) => void
}
export default function ShortText(props: ShortTextProps): JSX.Element {
- return <input type="text" className="short_text" placeholder="Enter Text..." onChange={props.handler}/>;
+ return <input type="text" css={textInputs} placeholder="Enter Text..." onChange={props.handler}/>;
}
diff --git a/src/components/InputTypes/TextArea.tsx b/src/components/InputTypes/TextArea.tsx
index 550adea..2b7a5f6 100644
--- a/src/components/InputTypes/TextArea.tsx
+++ b/src/components/InputTypes/TextArea.tsx
@@ -1,11 +1,21 @@
/** @jsx jsx */
-import { jsx } from "@emotion/react";
+import { jsx, css } from "@emotion/react";
import React, { ChangeEvent } from "react";
+import { textInputs } from "../../commonStyles";
interface TextAreaProps {
handler: (event: ChangeEvent<HTMLTextAreaElement>) => void
}
+const styles = css`
+ min-height: 20rem;
+ min-width: 40%;
+ width: 100%;
+ box-sizing: border-box;
+
+ padding: 1rem;
+`;
+
export default function TextArea(props: TextAreaProps): JSX.Element {
- return <textarea className="text_area" placeholder="Enter Text..." onChange={props.handler}/>;
+ return <textarea css={[textInputs, styles]} placeholder="Enter Text..." onChange={props.handler}/>;
}
diff --git a/src/components/Question.tsx b/src/components/Question.tsx
index 1c0fb31..66c1668 100644
--- a/src/components/Question.tsx
+++ b/src/components/Question.tsx
@@ -1,8 +1,9 @@
/** @jsx jsx */
-import { jsx } from "@emotion/react";
+import { jsx, css } from "@emotion/react";
import React, { ChangeEvent } from "react";
import { Question, QuestionType } from "../api/question";
+import { selectable } from "../commonStyles";
import create_input from "./InputTypes";
const _skip_normal_state: Array<QuestionType> = [
@@ -37,6 +38,7 @@ class RenderedQuestion extends React.Component<QuestionProp> {
this.props.public_state.set(target, value);
}
+ // This is here to allow dynamic selection between the general handler, and the textarea handler.
handler(_: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>): void {} // eslint-disable-line
normal_handler(event: ChangeEvent<HTMLInputElement>): void {
@@ -71,8 +73,8 @@ class RenderedQuestion extends React.Component<QuestionProp> {
// 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");
+ event.target.parentElement.classList.toggle("unselected");
+ event.target.parentElement.classList.toggle("selected");
}
}
@@ -109,17 +111,56 @@ class RenderedQuestion extends React.Component<QuestionProp> {
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"/>
+ const styles = css`
+ h1 {
+ margin-bottom: 0;
+ }
+
+ h3 {
+ margin-top: 0;
+ }
+
+ h1, h3 {
+ text-align: center;
+ padding: 0 2rem;
+ }
+
+ @media (max-width: 500px) {
+ h1, h3 {
+ padding: 0;
+ }
+ }
+ `;
+
+ return <div css={styles}>
+ <h1 css={selectable}>{question.name}</h1>
+ { question.data["text"] ? <h3 css={selectable}>{question.data["text"]}</h3> : "" }
+ <hr css={css`color: gray; margin: 3rem 0;`}/>
</div>;
+
} else {
+ const requiredStarStyles = css`
+ span {
+ display: none;
+ }
+
+ .required {
+ display: inline-block;
+ position: relative;
+
+ color: red;
+
+ top: -0.2rem;
+ margin-left: 0.2rem;
+ }
+ `;
+
return <div>
- <h2 className="selectable">
- {question.name}<span id={question.required ? "required" : ""} className="required_star">*</span>
+ <h2 css={[selectable, requiredStarStyles]}>
+ {question.name}<span className={question.required ? "required" : ""}>*</span>
</h2>
- { create_input(this.props, this.handler) }<hr/>
+ { create_input(this.props, this.handler) }
+ <hr css={css`color: gray; margin: 3rem 0;`}/>
</div>;
}
}
diff --git a/src/components/ScrollToTop.tsx b/src/components/ScrollToTop.tsx
index 6af938d..4888dec 100644
--- a/src/components/ScrollToTop.tsx
+++ b/src/components/ScrollToTop.tsx
@@ -1,6 +1,7 @@
/** @jsx jsx */
import { jsx, css } from "@emotion/react";
import React from "react";
+import colors from "../colors";
const styles = css`
width: 2.5rem;
@@ -10,7 +11,7 @@ const styles = css`
bottom: 3rem;
right: 3rem;
- background-color: #7289DA; /* Blurple */
+ background-color: ${colors.blurple};
border-radius: 50%;
opacity: 0;
diff --git a/src/pages/FormPage.tsx b/src/pages/FormPage.tsx
index b966e84..647003f 100644
--- a/src/pages/FormPage.tsx
+++ b/src/pages/FormPage.tsx
@@ -11,12 +11,143 @@ import Loading from "../components/Loading";
import ScrollToTop from "../components/ScrollToTop";
import { Form, FormFeatures, getForm } from "../api/forms";
+import colors from "../colors";
+import { unselectable } from "../commonStyles";
interface PathParams {
id: string
}
+interface NavigationProps {
+ form_state: boolean // Whether the form is open or not
+}
+
+class Navigation extends React.Component<NavigationProps> {
+ containerStyles = css`
+ margin: auto;
+ width: 50%;
+
+ text-align: center;
+ font-size: 1.5rem;
+ white-space: nowrap;
+
+ > div {
+ display: inline-block;
+ margin: 2rem auto;
+ width: 50%;
+ }
+
+ @media (max-width: 850px) {
+ width: 100%;
+
+ > div {
+ display: flex;
+ justify-content: center;
+
+ margin: 0 auto;
+ }
+ }
+
+ .return_button {
+ text-align: left;
+ }
+
+ .return_button.closed {
+ text-align: center;
+ }
+ `;
+
+ separatorStyles = css`
+ height: 0;
+ display: none;
+
+ @media (max-width: 850px) {
+ display: block;
+ }
+ `;
+
+ returnStyles = css`
+ padding: 0.5rem 2rem;
+ border-radius: 8px;
+
+ color: white;
+ text-decoration: none;
+
+ background-color: ${colors.greyple};
+ transition: background-color 300ms;
+
+ :hover {
+ background-color: ${colors.darkerGreyple};
+ }
+ }
+ `;
+
+ submitStyles = css`
+ text-align: right;
+
+ button {
+ padding: 0.5rem 4rem;
+ cursor: pointer;
+
+ border: none;
+ border-radius: 8px;
+
+ color: white;
+ font: inherit;
+
+ background-color: ${colors.blurple};
+ transition: background-color 300ms;
+ }
+
+ button:hover {
+ background-color: ${colors.darkerBlurple};
+ }
+ `;
+
+ render(): JSX.Element {
+ let submit = null;
+ if (this.props.form_state) {
+ submit = (
+ <div css={this.submitStyles}>
+ <button form="form" type="submit">Submit</button>
+ </div>
+ );
+ }
+
+ return (
+ <div css={[unselectable, this.containerStyles]}>
+ <div className={ "return_button" + (this.props.form_state ? "" : " closed") }>
+ <Link to="/" css={this.returnStyles}>Return Home</Link>
+ </div>
+ <br css={this.separatorStyles}/>
+ { submit }
+ </div>
+ );
+ }
+}
+
+const formStyles = css`
+ margin: auto;
+ width: 50%;
+
+ @media (max-width: 800px) {
+ /* Make form larger on mobile and tablet screens */
+ width: 80%;
+ }
+`;
+
+const closedHeaderStyles = css`
+ margin-bottom: 2rem;
+ padding: 1rem 4rem;
+ border-radius: 8px;
+
+ text-align: center;
+ font-size: 1.5rem;
+
+ background-color: ${colors.error};
+`;
+
function FormPage(): JSX.Element {
const { id } = useParams<PathParams>();
@@ -33,7 +164,7 @@ function FormPage(): JSX.Element {
}
const questions = form.questions.map((question, index) => {
- return <RenderedQuestion question={question} public_state={new Map()} key={index}/>;
+ return <RenderedQuestion question={question} public_state={new Map()} key={index + Date.now()}/>;
});
function handleSubmit(event: SyntheticEvent) {
@@ -53,39 +184,22 @@ function FormPage(): JSX.Element {
const open: boolean = form.features.includes(FormFeatures.Open);
let closed_header = null;
- let submit = null;
-
- if (open) {
- submit = (
- <div className="submit_form">
- <button form="form" type="submit">Submit</button>
- </div>
- );
- } else {
- closed_header = (
- <div className="closed_header">
- <div>This form is now closed. You will not be able to submit your response.</div>
- </div>
- );
+ if (!open) {
+ closed_header = <div css={closedHeaderStyles}>This form is now closed. You will not be able to submit your response.</div>;
}
-
return (
<div>
- <HeaderBar title={form.name} description={form.description} key={2}/>
- <div css={css`${require("./css/FormPage.css")};`}>
- <form id="form" onSubmit={handleSubmit} className="unselectable">
+ <HeaderBar title={form.name} description={form.description}/>
+
+ <div>
+ <form id="form" onSubmit={handleSubmit} css={[formStyles, unselectable]}>
{ closed_header }
- {questions}
+ { questions }
</form>
- <div className="nav unselectable">
- <div className={ "nav_buttons" + (open ? "" : " closed") }>
- <Link to="/" className="return_home">Return Home</Link>
- </div>
- <br className="nav_separator"/>
- { submit }
- </div>
+ <Navigation form_state={open}/>
</div>
+
<div css={css`margin-bottom: 10rem`}/>
<ScrollToTop/>
</div>
diff --git a/src/pages/css/FormPage.css b/src/pages/css/FormPage.css
deleted file mode 100644
index 254ddef..0000000
--- a/src/pages/css/FormPage.css
+++ /dev/null
@@ -1,452 +0,0 @@
-form {
- margin: auto;
- width: 50%;
-}
-
-@media (max-width: 800px) {
- /* Make form larger on mobile and tablet screens */
- form {
- width: 80%;
- }
-}
-
-hr {
- color: gray;
- margin: 3rem 0;
-}
-
-h1 {
- font-size: 2.5rem;
- margin-bottom: 0;
- text-align: center;
-}
-
-h3 {
- margin-top: 0;
- text-align: center;
-}
-
-.section_header {
- margin-top: 1rem;
-}
-
-.closed_header {
- margin-bottom: 2rem;
- text-align: center;
-}
-
-.closed_header div {
- font-size: 1.5rem;
- background-color: #f04747;
-
- padding: 1rem 4rem;
- border-radius: 8px;
-}
-
-.unselectable {
- -moz-user-select: none;
- -webkit-user-select: none;
- -ms-user-select: none;
- user-select: none;
-}
-
-.selectable {
- -moz-user-select: text;
- -webkit-user-select: text;
- -ms-user-select: text;
- user-select: text;
-}
-
-/* ------------------------------------------------------------- */
-/* Required */
-/* ------------------------------------------------------------- */
-.required_star {
- display: none;
-}
-
-#required.required_star {
- display: inline-block;
- position: relative;
-
- color: red;
-
- top: -0.2rem;
- margin-left: 0.2rem;
-}
-
-/* ------------------------------------------------------------- */
-/* Checkboxes */
-/* ------------------------------------------------------------- */
-.checkbox_label {
- display: inline-block;
- position: relative;
- top: 0.25em;
-
- width: 1em;
- height: 1em;
-
- margin: 1rem 0.5rem 0 0;
- border: whitesmoke 0.2rem solid;
- border-radius: 25%;
-
- -webkit-transition: background-color 300ms ease-in-out;
- transition: background-color 300ms ease-in-out;
-}
-
-.checkbox_label input {
- position: absolute;
- opacity: 0;
- height: 0;
- width: 0;
-}
-
-.unselected_checkbox_label {
- background-color: white;
-}
-
-.unselected_checkbox_label:hover {
- background-color: lightgray;
-}
-
-.selected_checkbox_label {
- background-color: #7289DA; /* Blurple */
-}
-
-.checkmark_span {
- position: absolute;
-}
-
-.selected_checkbox_label .checkmark_span {
- width: 0.30rem;
- height: 0.60rem;
- left: 0.25em;
-
- border: solid white;
- border-width: 0 0.2rem 0.2rem 0;
-
- -webkit-transform: rotate(45deg);
- -ms-transform: rotate(45deg);
- transform: rotate(45deg);
-}
-
-/* ------------------------------------------------------------- */
-/* Radio */
-/* ------------------------------------------------------------- */
-input[type="radio"] {
- margin: 1rem 0.5rem 0 0;
-}
-
-/* ------------------------------------------------------------- */
-/* Select */
-/* ------------------------------------------------------------- */
-.select_container {
- display: inline-block;
- position: relative;
-
- width: min(20rem, 90%);
- height: 100%;
- min-height: 2rem;
-
- background: whitesmoke;
-
- color: black;
- text-align: center;
-
- margin-bottom: 0;
-
- border: 0.1rem solid black;
- border-radius: 8px;
-
- -webkit-transition: border-radius 400ms;
- transition: border-radius 400ms;
-}
-
-.select_container.active {
- height: auto;
- border-radius: 8px 8px 0 0;
-}
-
-.select_arrow {
- display: inline-block;
- height: 0.5rem;
- width: 0.5rem;
-
- position: relative;
- float: right;
- right: 1em;
- top: 0.7rem;
-
- border: solid black;
- border-width: 0 0.2rem 0.2rem 0;
-
- -webkit-transform: rotate(45deg);
- -ms-transform: rotate(45deg);
- transform: rotate(45deg);
-
- -webkit-transition: transform 400ms;
- transition: transform 400ms;
-}
-
-.select_container.active .select_arrow {
- -webkit-transform: translateY(40%) rotate(225deg);
- -ms-transform: translateY(40%) rotate(225deg);
- transform: translateY(40%) rotate(225deg);
-}
-
-.selected_option {
- display: block;
- padding: 0.5rem 0;
-}
-
-.select_options_container {
- position: relative;
- width: 100%;
-
- /* Need to account for margin */
- left: -0.1rem;
-}
-
-.select_options {
- display: block;
- position: absolute;
- width: 100%;
-
- visibility: hidden;
- opacity: 0;
-
- background: whitesmoke;
- overflow: hidden;
-
- border: 0.1rem solid black;
- border-radius: 0 0 8px 8px;
- border-top: none;
-
- -webkit-transition: opacity 400ms, visibility 400ms;
- transition: opacity 400ms, visibility 400ms;
-}
-
-.select_container.active .select_options {
- visibility: visible;
- opacity: 1;
-}
-
-.select_options > div > div {
- padding: 0.75rem;
-}
-
-.select_options > div:hover {
- background-color: lightgray;
-}
-
-.select_options hr {
- margin: 0 1rem;
-}
-
-/* ------------------------------------------------------------- */
-/* Text Types */
-/* ------------------------------------------------------------- */
-.short_text, .text_area {
- display: inline-block;
- width: min(20rem, 90%);
- height: 100%;
- min-height: 2rem;
-
- background: whitesmoke;
-
- color: black;
- padding: 0 1rem;
- font: inherit;
-
- margin-bottom: 0;
-
- border: 0.1rem solid black;
- border-radius: 8px;
-}
-
-.text_area {
- min-height: 20rem;
- min-width: 40%;
- width: 100%;
- box-sizing: border-box;
-
- padding: 1rem;
-}
-
-/* ------------------------------------------------------------- */
-/* Range */
-/* ------------------------------------------------------------- */
-.range {
- display: flex;
- justify-content: space-between;
- position: relative;
- width: 100%;
-}
-
-.range label {
- width: 1rem;
-}
-
-.range label span {
- display: inline-block;
- transform: translateX(-50%);
- margin: 0 50%;
-
- white-space: nowrap;
-
- transition: transform 300ms;
-}
-
-.range_dot {
- width: 0.8rem;
- height: 0.8rem;
- background-color: whitesmoke;
-
- border: 0.2rem solid whitesmoke;
- border-radius: 50%;
-
- transition: background-color 300ms;
-}
-
-.range_dot.selected {
- background-color: #7289DA; /* Blurple */
-}
-
-.range_slider_container {
- display: flex;
- justify-content: center;
- width: 100%;
-
- position: absolute;
- z-index: -1;
-
- top: 2rem;
-
- transition: all 300ms;
-}
-
-.range_slider {
- width: 98%; /* Needs to be slightly smaller than container to work on all devices */
- height: 0.5rem;
- background-color: whitesmoke;
-
- transition: transform 300ms;
-}
-
-/* ------------------------------------------------------------- */
-/* Mobile Range */
-/* ------------------------------------------------------------- */
-@media (max-width: 800px){
- .range {
- width: 20%;
- display: block;
- margin: 0 auto;
- }
-
- .range_dot {
- margin-bottom: 1.5rem;
- }
-
- .range label span {
- margin-left: 0;
- transform: translateY(1.6rem) translateX(2rem);
- }
-
- .range_slider_container {
- width: 0.5rem;
- left: 0.32rem;
- height: 88%;
-
- background: whitesmoke;
- z-index: -1;
- }
-
- .range_slider {
- display: none;
- }
-}
-
-/* ------------------------------------------------------------- */
-/* Navigation */
-/* ------------------------------------------------------------- */
-.nav {
- margin: auto;
- width: 50%;
-
- text-align: center;
- font-size: 1.5rem;
- white-space: nowrap;
-}
-
-.nav_separator {
- height: 0;
- display: none;
-}
-
-.nav > div {
- display: inline-block;
- margin: 2rem auto;
- width: 50%;
-}
-
-.nav_buttons {
- text-align: left;
-}
-
-.nav_buttons.closed {
- text-align: center;
-}
-
-.submit_form {
- text-align: right;
-}
-
-/* Tile Buttons Vertically On Smaller Devices */
-@media (max-width: 850px) {
- .nav {
- width: 100%;
- }
-
- .nav_separator {
- display: block;
- }
-
- .nav > div {
- display: flex;
- justify-content: center;
-
- margin: 0 auto;
- }
-}
-
-.return_home {
- padding: 0.5rem 2rem;
- border-radius: 8px;
-
- color: white;
- text-decoration: none;
-
- background-color: #99AAB5; /* Gray-ish */
- transition: background-color 300ms;
-}
-
-.return_home:hover {
- background-color: #6E7D88; /* Darker gray-ish */
-}
-
-.submit_form button {
- padding: 0.5rem 4rem;
- cursor: pointer;
-
- border: none;
- border-radius: 8px;
-
- color: white;
- font: inherit;
-
- background-color: #7289DA; /* Blurple */
- transition: background-color 300ms;
-}
-
-.submit_form button:hover {
- background-color: #4E609C; /* Darker blurple */
-}
diff --git a/src/tests/pages/FormPage.test.tsx b/src/tests/pages/FormPage.test.tsx
index f7ecc32..62577cd 100644
--- a/src/tests/pages/FormPage.test.tsx
+++ b/src/tests/pages/FormPage.test.tsx
@@ -27,6 +27,6 @@ test("calls api method to load form", () => {
Object.defineProperty(forms, "getForm", {value: jest.fn(oldImpl)});
render(<Router><Route history={history}><FormPage /></Route></Router>);
-
+
expect(forms.getForm).toBeCalled();
});
diff --git a/webpack.config.js b/webpack.config.js
index 1264ba1..93e7e9d 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -65,12 +65,6 @@ module.exports = {
}, {
test: /\.svg$/,
loader: '@svgr/webpack'
- }, {
- test: /\.css$/,
- loader: 'raw-loader',
- options: {
- esModule: false
- }
}]
},
devServer: {
diff --git a/yarn.lock b/yarn.lock
index e1c7fde..2e4a587 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7343,14 +7343,6 @@ [email protected]:
iconv-lite "0.4.24"
unpipe "1.0.0"
- version "4.0.2"
- resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-4.0.2.tgz#1aac6b7d1ad1501e66efdac1522c73e59a584eb6"
- integrity sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==
- dependencies:
- loader-utils "^2.0.0"
- schema-utils "^3.0.0"
-
version "2.0.0"
resolved "https://registry.yarnpkg.com/react-app-polyfill/-/react-app-polyfill-2.0.0.tgz#a0bea50f078b8a082970a9d853dc34b6dcc6a3cf"