diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/colors.ts | 2 | ||||
-rw-r--r-- | src/commonStyles.tsx | 39 | ||||
-rw-r--r-- | src/components/InputTypes/Checkbox.tsx | 62 | ||||
-rw-r--r-- | src/components/InputTypes/Radio.tsx | 4 | ||||
-rw-r--r-- | src/components/InputTypes/Range.tsx | 101 | ||||
-rw-r--r-- | src/components/InputTypes/Select.tsx | 109 | ||||
-rw-r--r-- | src/components/InputTypes/ShortText.tsx | 3 | ||||
-rw-r--r-- | src/components/InputTypes/TextArea.tsx | 14 | ||||
-rw-r--r-- | src/components/Question.tsx | 61 | ||||
-rw-r--r-- | src/components/ScrollToTop.tsx | 3 | ||||
-rw-r--r-- | src/pages/FormPage.tsx | 168 | ||||
-rw-r--r-- | src/pages/css/FormPage.css | 452 | ||||
-rw-r--r-- | src/tests/pages/FormPage.test.tsx | 2 |
13 files changed, 498 insertions, 522 deletions
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(); }); |