aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--package.json26
-rw-r--r--src/api/forms.ts3
-rw-r--r--src/commonStyles.tsx10
-rw-r--r--src/components/ErrorMessage.tsx24
-rw-r--r--src/components/InputTypes/Radio.tsx5
-rw-r--r--src/components/InputTypes/Range.tsx6
-rw-r--r--src/components/InputTypes/Select.tsx18
-rw-r--r--src/components/InputTypes/ShortText.tsx14
-rw-r--r--src/components/InputTypes/TextArea.tsx14
-rw-r--r--src/components/InputTypes/index.tsx19
-rw-r--r--src/components/Question.tsx112
-rw-r--r--src/pages/FormPage.tsx124
-rw-r--r--src/tests/components/FormListing.test.tsx6
-rw-r--r--src/tests/pages/LandingPage.test.tsx3
-rw-r--r--yarn.lock285
15 files changed, 472 insertions, 197 deletions
diff --git a/package.json b/package.json
index cbf8293..5b8fd27 100644
--- a/package.json
+++ b/package.json
@@ -9,24 +9,24 @@
"@fortawesome/free-brands-svg-icons": "5.15.2",
"@fortawesome/free-solid-svg-icons": "5.15.2",
"@fortawesome/react-fontawesome": "0.1.14",
- "@sentry/react": "6.1.0",
+ "@sentry/react": "6.2.0",
"@svgr/webpack": "5.5.0",
"@swc/core": "1.2.47",
"axios": "0.21.1",
"copy-webpack-plugin": "7.0.0",
"fs-extra": "9.1.0",
- "html-webpack-plugin": "5.0.0",
+ "html-webpack-plugin": "5.2.0",
"identity-obj-proxy": "3.0.0",
"react": "17.0.1",
"react-app-polyfill": "2.0.0",
"react-dom": "17.0.1",
"react-router-dom": "5.2.0",
- "react-spinners": "0.10.4",
+ "react-spinners": "0.10.6",
"react-transition-group": "4.4.1",
"smoothscroll-polyfill": "0.4.4",
"swc-loader": "0.1.12",
- "typescript": "4.1.4",
- "webpack": "5.21.2",
+ "typescript": "4.2.3",
+ "webpack": "5.23.0",
"webpack-cli": "4.5.0",
"webpack-manifest-plugin": "3.0.0",
"workbox-webpack-plugin": "6.1.0"
@@ -57,18 +57,18 @@
"@swc/jest": "0.1.2",
"@testing-library/jest-dom": "5.11.9",
"@testing-library/react": "11.2.5",
- "@testing-library/user-event": "12.6.3",
+ "@testing-library/user-event": "12.8.1",
"@types/jest": "26.0.20",
- "@types/node": "14.14.25",
- "@types/react": "17.0.1",
- "@types/react-dom": "17.0.0",
+ "@types/node": "14.14.31",
+ "@types/react": "17.0.2",
+ "@types/react-dom": "17.0.1",
"@types/react-router-dom": "5.1.7",
- "@types/react-transition-group": "4.4.0",
+ "@types/react-transition-group": "4.4.1",
"@types/smoothscroll-polyfill": "0.3.1",
- "@typescript-eslint/eslint-plugin": "4.15.0",
- "@typescript-eslint/parser": "4.15.0",
+ "@typescript-eslint/eslint-plugin": "4.16.1",
+ "@typescript-eslint/parser": "4.16.1",
"dotenv": "8.2.0",
- "eslint": "7.19.0",
+ "eslint": "7.21.0",
"eslint-plugin-react": "7.22.0",
"husky": "5.0.9",
"jest": "26.6.3",
diff --git a/src/api/forms.ts b/src/api/forms.ts
index 12b9abf..77fbb8e 100644
--- a/src/api/forms.ts
+++ b/src/api/forms.ts
@@ -16,7 +16,8 @@ export interface Form {
webhook: WebHook | null,
questions: Array<Question>,
name: string,
- description: string
+ description: string,
+ submitted_text: string | null
}
export interface WebHook {
diff --git a/src/commonStyles.tsx b/src/commonStyles.tsx
index 89a2746..b2969f8 100644
--- a/src/commonStyles.tsx
+++ b/src/commonStyles.tsx
@@ -1,4 +1,5 @@
import { css } from "@emotion/react";
+import colors from "./colors";
const selectable = css`
-moz-user-select: text;
@@ -50,6 +51,14 @@ const textInputs = css`
border-radius: 8px;
`;
+const invalidStyles = css`
+ .invalid-box {
+ -webkit-appearance: none;
+ -webkit-box-shadow: 0 0 0.6rem ${colors.error};
+ box-shadow: 0 0 0.6rem ${colors.error};
+ border-color: transparent;
+ }
+`;
export {
selectable,
@@ -57,4 +66,5 @@ export {
hiddenInput,
multiSelectInput,
textInputs,
+ invalidStyles
};
diff --git a/src/components/ErrorMessage.tsx b/src/components/ErrorMessage.tsx
new file mode 100644
index 0000000..650100d
--- /dev/null
+++ b/src/components/ErrorMessage.tsx
@@ -0,0 +1,24 @@
+/** @jsx jsx */
+import { jsx, css } from "@emotion/react";
+import colors from "../colors";
+
+interface ErrorMessageProps {
+ show: boolean,
+ message: string
+}
+
+export default function ErrorMessage(props: ErrorMessageProps): JSX.Element | null {
+ const styles = css`
+ color: ${colors.error};
+ font-size: 1.15rem;
+ line-height: 1.1rem;
+ margin: 1rem 0 0;
+ visibility: ${props.show ? "visible" : "hidden"};
+ position: absolute;
+ z-index: -1;
+ `;
+
+ return (
+ <p css={styles}>{props.message}</p>
+ );
+}
diff --git a/src/components/InputTypes/Radio.tsx b/src/components/InputTypes/Radio.tsx
index 3bf13ed..a857964 100644
--- a/src/components/InputTypes/Radio.tsx
+++ b/src/components/InputTypes/Radio.tsx
@@ -7,7 +7,8 @@ import { multiSelectInput, hiddenInput } from "../../commonStyles";
interface RadioProps {
option: string,
question_id: string,
- handler: (event: ChangeEvent<HTMLInputElement>) => void
+ handler: (event: ChangeEvent<HTMLInputElement>) => void,
+ onBlurHandler: () => void
}
const styles = css`
@@ -31,7 +32,7 @@ const styles = css`
export default function Radio(props: RadioProps): JSX.Element {
return (
<label css={styles}>
- <input type="radio" name={props.question_id} onChange={props.handler} css={hiddenInput}/>
+ <input type="radio" name={props.question_id} onChange={props.handler} css={hiddenInput} onBlur={props.onBlurHandler}/>
<div css={multiSelectInput}/>
{props.option}<br/>
</label>
diff --git a/src/components/InputTypes/Range.tsx b/src/components/InputTypes/Range.tsx
index e2f89f4..23cb3f6 100644
--- a/src/components/InputTypes/Range.tsx
+++ b/src/components/InputTypes/Range.tsx
@@ -7,7 +7,9 @@ import { hiddenInput, multiSelectInput } from "../../commonStyles";
interface RangeProps {
question_id: string,
options: Array<string>,
- handler: (event: ChangeEvent<HTMLInputElement>) => void
+ handler: (event: ChangeEvent<HTMLInputElement>) => void,
+ required: boolean,
+ onBlurHandler: () => void
}
const containerStyles = css`
@@ -99,7 +101,7 @@ export default function Range(props: RangeProps): JSX.Element {
return (
<label css={[selectorStyles, css`width: 1rem`]} key={index}>
<span css={optionStyles}>{option}</span>
- <input type="radio" name={props.question_id} css={hiddenInput} onChange={props.handler}/>
+ <input type="radio" name={props.question_id} css={hiddenInput} onChange={props.handler} onBlur={props.onBlurHandler}/>
<div css={multiSelectInput}/>
</label>
);
diff --git a/src/components/InputTypes/Select.tsx b/src/components/InputTypes/Select.tsx
index e753357..2d0187a 100644
--- a/src/components/InputTypes/Select.tsx
+++ b/src/components/InputTypes/Select.tsx
@@ -1,11 +1,13 @@
/** @jsx jsx */
import { jsx, css } from "@emotion/react";
import React from "react";
-import { hiddenInput } from "../../commonStyles";
+import { hiddenInput, invalidStyles } from "../../commonStyles";
interface SelectProps {
options: Array<string>,
- state_dict: Map<string, string | boolean | null>
+ state_dict: Map<string, string | boolean | null>,
+ valid: boolean,
+ onBlurHandler: () => void
}
const containerStyles = css`
@@ -175,6 +177,14 @@ class Select extends React.Component<SelectProps> {
}
}
+ focusOption(): void {
+ if (!this.props.state_dict.get("value")) {
+ this.props.state_dict.set("value", "temporary");
+ this.props.onBlurHandler();
+ this.props.state_dict.set("value", null);
+ }
+ }
+
render(): JSX.Element {
const container_ref: React.RefObject<HTMLDivElement> = React.createRef();
const selected_option_ref: React.RefObject<HTMLDivElement> = React.createRef();
@@ -182,8 +192,8 @@ class Select extends React.Component<SelectProps> {
const handle_click = (event: React.MouseEvent<HTMLDivElement> | React.KeyboardEvent<HTMLDivElement>) => this.handle_click(container_ref, selected_option_ref, event);
return (
- <div css={[containerStyles, arrowStyles, optionContainerStyles]} ref={container_ref}>
- <div className="selected_container" css={mainWindowStyles}>
+ <div css={[containerStyles, arrowStyles, optionContainerStyles, invalidStyles]} onFocus={this.focusOption.bind(this)} ref={container_ref} onBlur={this.props.onBlurHandler}>
+ <div css={mainWindowStyles} className={!this.props.valid ? "invalid-box selected_container" : "selected_container"}>
<span className="arrow"/>
<div tabIndex={0} className="selected_option" ref={selected_option_ref} onMouseDown={handle_click} onKeyDown={handle_click}>...</div>
</div>
diff --git a/src/components/InputTypes/ShortText.tsx b/src/components/InputTypes/ShortText.tsx
index 1e38bcd..8d99dc6 100644
--- a/src/components/InputTypes/ShortText.tsx
+++ b/src/components/InputTypes/ShortText.tsx
@@ -1,12 +1,20 @@
/** @jsx jsx */
import { jsx } from "@emotion/react";
import React, { ChangeEvent } from "react";
-import { textInputs } from "../../commonStyles";
+import { textInputs, invalidStyles } from "../../commonStyles";
interface ShortTextProps {
- handler: (event: ChangeEvent<HTMLInputElement>) => void
+ handler: (event: ChangeEvent<HTMLInputElement>) => void,
+ onBlurHandler: () => void,
+ valid: boolean,
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ focus_ref: React.RefObject<any>
}
export default function ShortText(props: ShortTextProps): JSX.Element {
- return <input type="text" css={textInputs} placeholder="Enter Text..." onChange={props.handler}/>;
+ return (
+ <div css={invalidStyles}>
+ <input type="text" css={textInputs} placeholder="Enter Text..." onChange={props.handler} onBlur={props.onBlurHandler} className={!props.valid ? "invalid-box" : ""} ref={props.focus_ref}/>
+ </div>
+ );
}
diff --git a/src/components/InputTypes/TextArea.tsx b/src/components/InputTypes/TextArea.tsx
index 6e46c27..08424fb 100644
--- a/src/components/InputTypes/TextArea.tsx
+++ b/src/components/InputTypes/TextArea.tsx
@@ -1,10 +1,14 @@
/** @jsx jsx */
import { jsx, css } from "@emotion/react";
import React, { ChangeEvent } from "react";
-import { textInputs } from "../../commonStyles";
+import { invalidStyles, textInputs } from "../../commonStyles";
interface TextAreaProps {
- handler: (event: ChangeEvent<HTMLTextAreaElement>) => void
+ handler: (event: ChangeEvent<HTMLTextAreaElement>) => void,
+ onBlurHandler: () => void,
+ valid: boolean,
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ focus_ref: React.RefObject<any>
}
const styles = css`
@@ -17,5 +21,9 @@ const styles = css`
`;
export default function TextArea(props: TextAreaProps): JSX.Element {
- return <textarea css={[textInputs, styles]} placeholder="Enter Text..." onChange={props.handler}/>;
+ return (
+ <div css={invalidStyles}>
+ <textarea css={[textInputs, styles]} placeholder="Enter Text..." onChange={props.handler} onBlur={props.onBlurHandler} className={!props.valid ? "invalid-box" : ""} ref={props.focus_ref}/>
+ </div>
+ );
}
diff --git a/src/components/InputTypes/index.tsx b/src/components/InputTypes/index.tsx
index f1e0b30..bc65248 100644
--- a/src/components/InputTypes/index.tsx
+++ b/src/components/InputTypes/index.tsx
@@ -18,12 +18,17 @@ const require_options: Array<QuestionType> = [
QuestionType.Range
];
-export default function create_input({ question, public_state }: QuestionProp, handler: (event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => void): JSX.Element | JSX.Element[] {
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export default function create_input({ question, public_state }: QuestionProp, handler: (event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => void, onBlurHandler: () => void, focus_ref: React.RefObject<any>): JSX.Element | JSX.Element[] {
let result: JSX.Element | JSX.Element[];
// eslint-disable-next-line
// @ts-ignore
let options: string[] = question.data["options"];
+ let valid = true;
+ if (!public_state.get("valid")) {
+ valid = false;
+ }
// Catch input types that require options but don't have any
if ((options === undefined || typeof options !== "object") && require_options.includes(question.type)) {
@@ -34,7 +39,7 @@ export default function create_input({ question, public_state }: QuestionProp, h
/* eslint-disable react/react-in-jsx-scope */
switch (question.type) {
case QuestionType.TextArea:
- result = <TextArea handler={handler}/>;
+ result = <TextArea handler={handler} valid={valid} onBlurHandler={onBlurHandler} focus_ref={focus_ref}/>;
break;
case QuestionType.Checkbox:
@@ -42,19 +47,19 @@ export default function create_input({ question, public_state }: QuestionProp, h
break;
case QuestionType.Radio:
- result = options.map((option, index) => <Radio option={option} question_id={question.id} handler={handler} key={index}/>);
+ result = options.map((option, index) => <Radio option={option} question_id={question.id} handler={handler} key={index} onBlurHandler={onBlurHandler}/>);
break;
case QuestionType.Select:
- result = <Select options={options} state_dict={public_state}/>;
+ result = <Select options={options} state_dict={public_state} valid={valid} onBlurHandler={onBlurHandler}/>;
break;
case QuestionType.ShortText:
- result = <ShortText handler={handler}/>;
+ result = <ShortText handler={handler} onBlurHandler={onBlurHandler} valid={valid} focus_ref={focus_ref}/>;
break;
case QuestionType.Range:
- result = <Range question_id={question.id} options={options} handler={handler}/>;
+ result = <Range question_id={question.id} options={options} handler={handler} required={question.required} onBlurHandler={onBlurHandler}/>;
break;
case QuestionType.Code:
@@ -63,7 +68,7 @@ export default function create_input({ question, public_state }: QuestionProp, h
break;
default:
- result = <TextArea handler={handler}/>;
+ result = <TextArea handler={handler} valid={valid} onBlurHandler={onBlurHandler} focus_ref={focus_ref}/>;
}
/* eslint-enable react/react-in-jsx-scope */
diff --git a/src/components/Question.tsx b/src/components/Question.tsx
index 735d69b..0af745e 100644
--- a/src/components/Question.tsx
+++ b/src/components/Question.tsx
@@ -5,6 +5,7 @@ import React, { ChangeEvent } from "react";
import { Question, QuestionType } from "../api/question";
import { selectable } from "../commonStyles";
import create_input from "./InputTypes";
+import ErrorMessage from "./ErrorMessage";
const skip_normal_state: Array<QuestionType> = [
QuestionType.Radio,
@@ -17,6 +18,9 @@ const skip_normal_state: Array<QuestionType> = [
export type QuestionProp = {
question: Question,
public_state: Map<string, string | boolean | null>,
+ scroll_ref: React.RefObject<HTMLDivElement>,
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ focus_ref: React.RefObject<any>
}
class RenderedQuestion extends React.Component<QuestionProp> {
@@ -27,6 +31,10 @@ class RenderedQuestion extends React.Component<QuestionProp> {
} else {
this.handler = this.normal_handler.bind(this);
}
+ this.blurHandler = this.blurHandler.bind(this);
+
+ this.setPublicState("valid", true);
+ this.setPublicState("error", "");
if (!skip_normal_state.includes(props.question.type)) {
this.setPublicState("value", "");
@@ -41,6 +49,18 @@ class RenderedQuestion extends React.Component<QuestionProp> {
// This is here to allow dynamic selection between the general handler, and the textarea handler.
handler(_: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>): void {} // eslint-disable-line
+ blurHandler(): void {
+ if (this.props.question.required) {
+ if (!this.props.public_state.get("value")) {
+ this.setPublicState("error", "Field must be filled.");
+ this.setPublicState("valid", false);
+ } else {
+ this.setPublicState("error", "");
+ this.setPublicState("valid", true);
+ }
+ }
+ }
+
normal_handler(event: ChangeEvent<HTMLInputElement>): void {
let target: string;
let value: string | boolean;
@@ -73,12 +93,90 @@ class RenderedQuestion extends React.Component<QuestionProp> {
event.target.parentElement.classList.toggle("unselected");
event.target.parentElement.classList.toggle("selected");
}
+
+ const options: string | string[] = this.props.question.data["options"];
+ switch (event.target.type) {
+ case "text":
+ this.setPublicState("valid", true);
+ break;
+
+ case "checkbox":
+ // We need to check this here, because checkbox doesn't have onBlur
+ if (this.props.question.required && typeof options !== "string") {
+ const keys: string[] = [];
+ options.forEach((val, index) => {
+ keys.push(`${("000" + index).slice(-4)}. ${val}`);
+ });
+ if (keys.every(v => !this.props.public_state.get(v))) {
+ this.setPublicState("error", "Field must be filled.");
+ this.setPublicState("valid", false);
+ } else {
+ this.setPublicState("error", "");
+ this.setPublicState("valid", true);
+ }
+ }
+ break;
+
+ case "radio":
+ this.setPublicState("valid", true);
+ this.setPublicState("error", "");
+ break;
+ }
}
text_area_handler(event: ChangeEvent<HTMLTextAreaElement>): void {
+ // We will validate again when focusing out.
+ this.setPublicState("valid", true);
+ this.setPublicState("error", "");
+
this.setPublicState("value", event.target.value);
}
+ validateField(): void {
+ if (!this.props.question.required) {
+ return;
+ }
+
+ let invalid = false;
+ const options: string | string[] = this.props.question.data["options"];
+ switch (this.props.question.type) {
+ case QuestionType.TextArea:
+ case QuestionType.ShortText:
+ if (this.props.public_state.get("value") === "") {
+ invalid = true;
+ }
+ break;
+
+ case QuestionType.Select:
+ case QuestionType.Range:
+ case QuestionType.Radio:
+ if (!this.props.public_state.get("value")) {
+ invalid = true;
+ }
+ break;
+
+ case QuestionType.Checkbox:
+ if (typeof options !== "string") {
+ const keys: string[] = [];
+ options.forEach((val, index) => {
+ keys.push(`${("000" + index).slice(-4)}. ${val}`);
+ });
+ if (keys.every(v => !this.props.public_state.get(v))) {
+ invalid = true;
+ }
+ }
+ break;
+ }
+
+ if (invalid) {
+ this.setPublicState("error", "Field must be filled.");
+ this.setPublicState("valid", false);
+ } else {
+ this.setPublicState("error", "");
+ this.setPublicState("valid", true);
+ }
+ }
+
componentDidMount(): void {
// Initialize defaults for complex and nested fields
const options: string | string[] = this.props.question.data["options"];
@@ -151,12 +249,22 @@ class RenderedQuestion extends React.Component<QuestionProp> {
margin-left: 0.2rem;
}
`;
+ let valid = true;
+ if (!this.props.public_state.get("valid")) {
+ valid = false;
+ }
+ const rawError = this.props.public_state.get("error");
+ let error = "";
+ if (typeof rawError === "string") {
+ error = rawError;
+ }
- return <div>
+ return <div ref={this.props.scroll_ref}>
<h2 css={[selectable, requiredStarStyles]}>
{question.name}<span className={question.required ? "required" : ""}>*</span>
</h2>
- { create_input(this.props, this.handler) }
+ { create_input(this.props, this.handler, this.blurHandler, this.props.focus_ref) }
+ <ErrorMessage show={!valid} message={error} />
<hr css={css`color: gray; margin: 3rem 0;`}/>
</div>;
}
diff --git a/src/pages/FormPage.tsx b/src/pages/FormPage.tsx
index c49b9fd..1e331b9 100644
--- a/src/pages/FormPage.tsx
+++ b/src/pages/FormPage.tsx
@@ -2,8 +2,9 @@
import { jsx, css } from "@emotion/react";
import { Link } from "react-router-dom";
-import React, { SyntheticEvent, useEffect, useState } from "react";
+import React, { SyntheticEvent, useEffect, useState, createRef } from "react";
import { useParams } from "react-router";
+import { PropagateLoader } from "react-spinners";
import HeaderBar from "../components/HeaderBar";
import RenderedQuestion from "../components/Question";
@@ -13,7 +14,8 @@ import ScrollToTop from "../components/ScrollToTop";
import { Form, FormFeatures, getForm } from "../api/forms";
import colors from "../colors";
import { unselectable } from "../commonStyles";
-
+import { Question, QuestionType } from "../api/question";
+import ApiClient from "../api/client";
interface PathParams {
id: string
@@ -24,13 +26,12 @@ interface NavigationProps {
}
class Navigation extends React.Component<NavigationProps> {
- containerStyles = css`
+ static containerStyles = css`
margin: auto;
width: 50%;
text-align: center;
font-size: 1.5rem;
- white-space: nowrap;
> div {
display: inline-block;
@@ -67,12 +68,13 @@ class Navigation extends React.Component<NavigationProps> {
}
`;
- returnStyles = css`
+ static returnStyles = css`
padding: 0.5rem 2rem;
border-radius: 8px;
color: white;
text-decoration: none;
+ white-space: nowrap;
background-color: ${colors.greyple};
transition: background-color 300ms;
@@ -80,11 +82,11 @@ class Navigation extends React.Component<NavigationProps> {
:hover {
background-color: ${colors.darkerGreyple};
}
- }
`;
submitStyles = css`
text-align: right;
+ white-space: nowrap;
button {
padding: 0.5rem 4rem;
@@ -116,9 +118,9 @@ class Navigation extends React.Component<NavigationProps> {
}
return (
- <div css={[unselectable, this.containerStyles]}>
+ <div css={[unselectable, Navigation.containerStyles]}>
<div className={ "return_button" + (this.props.form_state ? "" : " closed") }>
- <Link to="/" css={this.returnStyles}>Return Home</Link>
+ <Link to="/" css={Navigation.returnStyles}>Return Home</Link>
</div>
<br css={this.separatorStyles}/>
{ submit }
@@ -156,6 +158,8 @@ function FormPage(): JSX.Element {
const { id } = useParams<PathParams>();
const [form, setForm] = useState<Form>();
+ const [sending, setSending] = useState<boolean>();
+ const [sent, setSent] = useState<boolean>();
useEffect(() => {
getForm(id).then(form => {
@@ -163,26 +167,118 @@ function FormPage(): JSX.Element {
});
}, []);
+ if (form && sent) {
+ const thanksStyle = css`font-family: "Uni Sans", "Hind", "Arial", sans-serif; margin-top: 15.5rem;`;
+ const divStyle = css`width: 80%;`;
+ return (
+ <div>
+ <HeaderBar title={form.name} description={form.description}/>
+ <div css={[unselectable, Navigation.containerStyles, divStyle]}>
+ <h3 css={thanksStyle}>{form.submitted_text ?? "Thanks for your response!"}</h3>
+ <div className={ "return_button closed" }>
+ <Link to="/" css={Navigation.returnStyles}>Return Home</Link>
+ </div>
+ </div>
+ </div>
+ );
+ }
+
+ if (sending) {
+ return (
+ <div>
+ <HeaderBar title={"Submitting..."}/>
+ <div css={{display: "flex", justifyContent: "center", paddingTop: "40px"}}>
+ <PropagateLoader color="white"/>
+ </div>
+ </div>
+ );
+ }
+
if (!form) {
return <Loading/>;
}
+ const refMap: Map<string, React.RefObject<RenderedQuestion>> = new Map();
const questions = form.questions.map((question, index) => {
- return <RenderedQuestion question={question} public_state={new Map()} key={index + Date.now()}/>;
+ const questionRef = createRef<RenderedQuestion>();
+ refMap.set(question.id, questionRef);
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ return <RenderedQuestion ref={questionRef} focus_ref={createRef<any>()} scroll_ref={createRef<HTMLDivElement>()} question={question} public_state={new Map()} key={index + Date.now()}/>;
});
- function handleSubmit(event: SyntheticEvent) {
+ async function handleSubmit(event: SyntheticEvent) {
+ event.preventDefault();
+ // Client-side required validation
+ const invalidFieldIDs: number[] = [];
+ questions.forEach((prop, i) => {
+ const question: Question = prop.props.question;
+ if (!question.required) {
+ return;
+ }
+
+ const questionRef = refMap.get(question.id);
+ if (questionRef && questionRef.current) {
+ questionRef.current.validateField();
+ }
+ // In case when field is invalid, add this to invalid fields list.
+ if (prop.props.public_state.get("valid") === false) {
+ invalidFieldIDs.push(i);
+ }
+ });
+
+ if (invalidFieldIDs.length) {
+ const firstErrored = questions[invalidFieldIDs[0]];
+ if (firstErrored && firstErrored.props.scroll_ref) {
+ // If any element is already focused, unfocus it to avoid not scrolling behavior.
+ if (document.activeElement && document.activeElement instanceof HTMLElement) {
+ document.activeElement.blur();
+ }
+
+ firstErrored.props.scroll_ref.current.scrollIntoView({ behavior: "smooth", block: "center" });
+ if (firstErrored.props.focus_ref && firstErrored.props.focus_ref.current) {
+ firstErrored.props.focus_ref.current.focus({ preventScroll: true });
+ }
+ }
+ return;
+ }
+
+ setSending(true);
+
+ const answers: { [key: string]: unknown } = {};
questions.forEach(prop => {
- const question = prop.props.question;
+ const question: Question = prop.props.question;
+ const options: string | string[] = question.data["options"];
- // TODO: Parse input from each question, and submit
+ // Parse input from each question
switch (question.type) {
+ case QuestionType.Section:
+ answers[question.id] = false;
+ break;
+
+ case QuestionType.Checkbox: {
+ if (typeof options !== "string") {
+ const keys: Map<string, string> = new Map();
+ options.forEach((val: string, index) => {
+ keys.set(val, `${("000" + index).slice(-4)}. ${val}`);
+ });
+ const pairs: { [key: string]: boolean } = { };
+ keys.forEach((val, key) => {
+ pairs[key] = !!prop.props.public_state.get(val);
+ });
+ answers[question.id] = pairs;
+ }
+ break;
+ }
+
+ case QuestionType.Code:
default:
- console.log(question.id, prop.props.public_state);
+ answers[question.id] = prop.props.public_state.get("value");
}
});
- event.preventDefault();
+ await ApiClient.post(`forms/submit/${id}`, {response: answers});
+ setSending(false);
+ setSent(true);
}
const open: boolean = form.features.includes(FormFeatures.Open);
diff --git a/src/tests/components/FormListing.test.tsx b/src/tests/components/FormListing.test.tsx
index f269dbf..2116e48 100644
--- a/src/tests/components/FormListing.test.tsx
+++ b/src/tests/components/FormListing.test.tsx
@@ -21,7 +21,8 @@ const openFormListing: Form = {
required: false
}
],
- webhook: null
+ webhook: null,
+ submitted_text: null
};
const closedFormListing: Form = {
@@ -38,7 +39,8 @@ const closedFormListing: Form = {
required: false
}
],
- webhook: null
+ webhook: null,
+ submitted_text: null
};
test("renders form listing with specified title", () => {
diff --git a/src/tests/pages/LandingPage.test.tsx b/src/tests/pages/LandingPage.test.tsx
index e461815..6f8a530 100644
--- a/src/tests/pages/LandingPage.test.tsx
+++ b/src/tests/pages/LandingPage.test.tsx
@@ -21,7 +21,8 @@ const testingForm: forms.Form = {
required: true
}
],
- "webhook": null
+ "webhook": null,
+ submitted_text: null
};
test("renders landing page", () => {
diff --git a/yarn.lock b/yarn.lock
index cde8451..5dad1ca 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2,7 +2,7 @@
# yarn lockfile v1
-"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.11":
+"@babel/[email protected]", "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.11":
version "7.12.11"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f"
integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==
@@ -1209,10 +1209,10 @@
resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46"
integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==
-"@eslint/eslintrc@^0.3.0":
- version "0.3.0"
- resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.3.0.tgz#d736d6963d7003b6514e6324bec9c602ac340318"
- integrity sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg==
+"@eslint/eslintrc@^0.4.0":
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.0.tgz#99cc0a0584d72f1df38b900fb062ba995f395547"
+ integrity sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog==
dependencies:
ajv "^6.12.4"
debug "^4.1.1"
@@ -1221,7 +1221,6 @@
ignore "^4.0.6"
import-fresh "^3.2.1"
js-yaml "^3.13.1"
- lodash "^4.17.20"
minimatch "^3.0.4"
strip-json-comments "^3.1.1"
@@ -1631,68 +1630,68 @@
estree-walker "^1.0.1"
picomatch "^2.2.2"
-"@sentry/[email protected]":
- version "6.1.0"
- resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-6.1.0.tgz#0e18a07b44bebed729bcf842af33203e388e2053"
- integrity sha512-t3y2TLXDWgvfknyH8eKj/9mghJfSEqItFyp74zPu1Src6kOPjkd4Sa7o4+bdkNgA8dIIOrDAhRUbB2sq4sWMCA==
+"@sentry/[email protected]":
+ version "6.2.0"
+ resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-6.2.0.tgz#4113a92bc82f55e63f30cb16a94f717bd0b95817"
+ integrity sha512-4r3paHcHXLemj471BtNDhUs2kvJxk5XDRplz1dbC/LHXN5PWEXP4anhGILxOlxqi4y33r53PIZu3xXFjznaVZA==
dependencies:
- "@sentry/core" "6.1.0"
- "@sentry/types" "6.1.0"
- "@sentry/utils" "6.1.0"
+ "@sentry/core" "6.2.0"
+ "@sentry/types" "6.2.0"
+ "@sentry/utils" "6.2.0"
tslib "^1.9.3"
-"@sentry/[email protected]":
- version "6.1.0"
- resolved "https://registry.yarnpkg.com/@sentry/core/-/core-6.1.0.tgz#7dd4584dcaf2188a78b94b766068342e6ee65229"
- integrity sha512-57mXkp3NoyxRycXrL+Ec6bYS6UYJZp9tYX0lUp5Ry2M0FxDZ3Q4drkjr8MIQOhBaQXP2ukSX4QTVLGMPm60zMw==
+"@sentry/[email protected]":
+ version "6.2.0"
+ resolved "https://registry.yarnpkg.com/@sentry/core/-/core-6.2.0.tgz#be1c33854fc94e1a4d867f7c2c311cf1cefb8ee9"
+ integrity sha512-oTr2b25l+0bv/+d6IgMamPuGleWV7OgJb0NFfd+WZhw6UDRgr7CdEJy2gW6tK8SerwXgPHdn4ervxsT3WIBiXw==
dependencies:
- "@sentry/hub" "6.1.0"
- "@sentry/minimal" "6.1.0"
- "@sentry/types" "6.1.0"
- "@sentry/utils" "6.1.0"
+ "@sentry/hub" "6.2.0"
+ "@sentry/minimal" "6.2.0"
+ "@sentry/types" "6.2.0"
+ "@sentry/utils" "6.2.0"
tslib "^1.9.3"
-"@sentry/[email protected]":
- version "6.1.0"
- resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-6.1.0.tgz#fb22734c91c9d68564737996bf28b7e032e3d8ee"
- integrity sha512-JnBSCgNg3VHiMojUl5tCHU8iWPVuE+qqENIzG9A722oJms1kKWBvWl+yQzhWBNdgk5qeAY3F5UzKWJZkbJ6xow==
+"@sentry/[email protected]":
+ version "6.2.0"
+ resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-6.2.0.tgz#e7502652bc9608cf8fb63e43cd49df9019a28f29"
+ integrity sha512-BDTEFK8vlJydWXp/KMX0stvv73V7od224iLi+w3k7BcPwMKXBuURBXPU8d5XIC4G8nwg8X6cnDvwL+zBBlBbkg==
dependencies:
- "@sentry/types" "6.1.0"
- "@sentry/utils" "6.1.0"
+ "@sentry/types" "6.2.0"
+ "@sentry/utils" "6.2.0"
tslib "^1.9.3"
-"@sentry/[email protected]":
- version "6.1.0"
- resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-6.1.0.tgz#b3abf76d93b95477a3c1029db810818bdc56845f"
- integrity sha512-g6sfNKenL7wnsr/tibp8nFiMv/XRH0s0Pt4p151npmNI+SmjuUz3GGYEXk8ChCyaKldYKilkNOFdVXJxUf5gZw==
+"@sentry/[email protected]":
+ version "6.2.0"
+ resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-6.2.0.tgz#718b70babb55912eeb38babaf7823d1bcdd77d1e"
+ integrity sha512-haxsx8/ZafhZUaGeeMtY7bJt9HbDlqeiaXrRMp1CxGtd0ZRQwHt60imEjl6IH1I73SEWxNfqScGsX2s3HzztMg==
dependencies:
- "@sentry/hub" "6.1.0"
- "@sentry/types" "6.1.0"
+ "@sentry/hub" "6.2.0"
+ "@sentry/types" "6.2.0"
tslib "^1.9.3"
-"@sentry/[email protected]":
- version "6.1.0"
- resolved "https://registry.yarnpkg.com/@sentry/react/-/react-6.1.0.tgz#f01a5f330ee339743420056872d0ae606a85381d"
- integrity sha512-X1Jp1ueHsmHQXBcNWshA43MTdn0B3B06A36yv6mzAznmanpf+7+f7SHrnRFIwuBpTdPsHzolTmSE9VlkZPBgBg==
+"@sentry/[email protected]":
+ version "6.2.0"
+ resolved "https://registry.yarnpkg.com/@sentry/react/-/react-6.2.0.tgz#bf46c38762554f246ca9dec527e6a5b3168fb0b0"
+ integrity sha512-Jf3s7om1iLpApkN26O7c3Ult3lS91ekZNC4WKtcPb6b+KOBQ36sB0d1KhL3hGZ55UKLmgZu3jn2hd7bJ9EY3yA==
dependencies:
- "@sentry/browser" "6.1.0"
- "@sentry/minimal" "6.1.0"
- "@sentry/types" "6.1.0"
- "@sentry/utils" "6.1.0"
+ "@sentry/browser" "6.2.0"
+ "@sentry/minimal" "6.2.0"
+ "@sentry/types" "6.2.0"
+ "@sentry/utils" "6.2.0"
hoist-non-react-statics "^3.3.2"
tslib "^1.9.3"
-"@sentry/[email protected]":
- version "6.1.0"
- resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.1.0.tgz#5f9379229423ca1325acf6709e95687180f67132"
- integrity sha512-kIaN52Fw5K+2mKRaHE2YluJ+F/qMGSUzZXIFDNdC6OUMXQ4TM8gZTrITXs8CLDm7cK8iCqFCtzKOjKK6KyOKAg==
+"@sentry/[email protected]":
+ version "6.2.0"
+ resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.2.0.tgz#ca020ff42913c6b9f88a9d0c375b5ee3965a2590"
+ integrity sha512-vN4P/a+QqAuVfWFB9G3nQ7d6bgnM9jd/RLVi49owMuqvM24pv5mTQHUk2Hk4S3k7ConrHFl69E7xH6Dv5VpQnQ==
-"@sentry/[email protected]":
- version "6.1.0"
- resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-6.1.0.tgz#52e3d7050983e685d3a48f9d2efa1e6eb4ae3e6d"
- integrity sha512-6JAplzUOS6bEwfX0PDRZBbYRvn9EN22kZfcL0qGHtM9L0QQ5ybjbbVwOpbXgRkiZx++dQbzLFtelxnDhsbFG+Q==
+"@sentry/[email protected]":
+ version "6.2.0"
+ resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-6.2.0.tgz#39c81ad5ba92cec54d690e3fa8ea4e777d8e9c2b"
+ integrity sha512-YToUC7xYf2E/pIluI7upYTlj8fKXOtdwoOBkcQZifHgX/dP+qDaHibbBFe5PyZwdmU2UiLnWFsBr0gjo0QFo1g==
dependencies:
- "@sentry/types" "6.1.0"
+ "@sentry/types" "6.2.0"
tslib "^1.9.3"
"@sinonjs/commons@^1.7.0":
@@ -1925,10 +1924,10 @@
"@babel/runtime" "^7.12.5"
"@testing-library/dom" "^7.28.1"
-"@testing-library/[email protected]":
- version "12.6.3"
- resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-12.6.3.tgz#4a77c56a48823cf8adebd0f57670e4a89c24d058"
- integrity sha512-PCmbUKofE4SXA7l8jphZAbvv5H3c4ix34xPZ/GNe99fASX//msJRgiMbHIBP+GwRfgVG9c7zmkODSPu2X2vNRw==
+"@testing-library/[email protected]":
+ version "12.8.1"
+ resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-12.8.1.tgz#aa897d6e7f0cf2208385abc2da2ac3f5844bbd00"
+ integrity sha512-u521YhkCKip0DQNDpfj9V97PU7UlCTkW5jURUD4JipuVe/xDJ32dJSIHlT2pqAs/I91OFB8p6LtqaLZpOu8BWQ==
dependencies:
"@babel/runtime" "^7.12.5"
@@ -2071,10 +2070,10 @@
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
-"@types/node@*", "@types/[email protected]":
- version "14.14.25"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.25.tgz#15967a7b577ff81383f9b888aa6705d43fbbae93"
- integrity sha512-EPpXLOVqDvisVxtlbvzfyqSsFeQxltFbluZNRndIb8tr9KiBnYNLzrc1N3pyKUCww2RNrfHDViqDWWE1LCJQtQ==
+"@types/node@*", "@types/[email protected]":
+ version "14.14.31"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.31.tgz#72286bd33d137aa0d152d47ec7c1762563d34055"
+ integrity sha512-vFHy/ezP5qI0rFgJ7aQnjDXwAMrG0KqqIH7tQG5PPv3BWBayOPIQNBjVc/P6hhdZfMx51REc6tfDNXHUio893g==
"@types/normalize-package-data@^2.4.0":
version "2.4.0"
@@ -2101,10 +2100,10 @@
resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.4.tgz#15925414e0ad2cd765bfef58842f7e26a7accb24"
integrity sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==
- version "17.0.0"
- resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.0.tgz#b3b691eb956c4b3401777ee67b900cb28415d95a"
- integrity sha512-lUqY7OlkF/RbNtD5nIq7ot8NquXrdFrjSOR6+w9a9RFQevGi1oZO1dcJbXMeONAPKtZ2UrZOEJ5UOCVsxbLk/g==
+ version "17.0.1"
+ resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.1.tgz#d92d77d020bfb083e07cc8e0ac9f933599a4d56a"
+ integrity sha512-yIVyopxQb8IDZ7SOHeTovurFq+fXiPICa+GV3gp0Xedsl+MwQlMLKmvrnEjFbQxjliH5YVAEWFh975eVNmKj7Q==
dependencies:
"@types/react" "*"
@@ -2125,17 +2124,17 @@
"@types/history" "*"
"@types/react" "*"
- version "4.4.0"
- resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.0.tgz#882839db465df1320e4753e6e9f70ca7e9b4d46d"
- integrity sha512-/QfLHGpu+2fQOqQaXh8MG9q03bFENooTb/it4jr5kKaZlDQfWvjqWZg48AwzPVMBHlRuTRAY7hRHCEOXz5kV6w==
+ version "4.4.1"
+ resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.1.tgz#e1a3cb278df7f47f17b5082b1b3da17170bd44b1"
+ integrity sha512-vIo69qKKcYoJ8wKCJjwSgCTM+z3chw3g18dkrDfVX665tMH7tmbDxEAnPdey4gTlwZz5QuHGzd+hul0OVZDqqQ==
dependencies:
"@types/react" "*"
-"@types/react@*", "@types/[email protected]":
- version "17.0.1"
- resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.1.tgz#eb1f1407dea8da3bc741879c1192aa703ab9975b"
- integrity sha512-w8t9f53B2ei4jeOqf/gxtc2Sswnc3LBK5s0DyJcg5xd10tMHXts2N31cKjWfH9IC/JvEPa/YF1U4YeP1t4R6HQ==
+"@types/react@*", "@types/[email protected]":
+ version "17.0.2"
+ resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.2.tgz#3de24c4efef902dd9795a49c75f760cbe4f7a5a8"
+ integrity sha512-Xt40xQsrkdvjn1EyWe1Bc0dJLcil/9x2vAuW7ya+PuQip4UYUaXyhzWmAbwRsdMgwOFHpfp7/FFZebDU6Y8VHA==
dependencies:
"@types/prop-types" "*"
csstype "^3.0.2"
@@ -2188,13 +2187,13 @@
dependencies:
"@types/yargs-parser" "*"
-"@typescript-eslint/[email protected]":
- version "4.15.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.15.0.tgz#13a5a07cf30d0d5781e43480aa2a8d38d308b084"
- integrity sha512-DJgdGZW+8CFUTz5C/dnn4ONcUm2h2T0itWD85Ob5/V27Ndie8hUoX5HKyGssvR8sUMkAIlUc/AMK67Lqa3kBIQ==
+"@typescript-eslint/[email protected]":
+ version "4.16.1"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.16.1.tgz#2caf6a79dd19c3853b8d39769a27fccb24e4e651"
+ integrity sha512-SK777klBdlkUZpZLC1mPvyOWk9yAFCWmug13eAjVQ4/Q1LATE/NbcQL1xDHkptQkZOLnPmLUA1Y54m8dqYwnoQ==
dependencies:
- "@typescript-eslint/experimental-utils" "4.15.0"
- "@typescript-eslint/scope-manager" "4.15.0"
+ "@typescript-eslint/experimental-utils" "4.16.1"
+ "@typescript-eslint/scope-manager" "4.16.1"
debug "^4.1.1"
functional-red-black-tree "^1.0.1"
lodash "^4.17.15"
@@ -2202,60 +2201,60 @@
semver "^7.3.2"
tsutils "^3.17.1"
-"@typescript-eslint/[email protected]":
- version "4.15.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.15.0.tgz#b87c36410a9b23f637689427be85007a2ec1a9c6"
- integrity sha512-V4vaDWvxA2zgesg4KPgEGiomWEBpJXvY4ZX34Y3qxK8LUm5I87L+qGIOTd9tHZOARXNRt9pLbblSKiYBlGMawg==
+"@typescript-eslint/[email protected]":
+ version "4.16.1"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.16.1.tgz#da7a396dc7d0e01922acf102b76efff17320b328"
+ integrity sha512-0Hm3LSlMYFK17jO4iY3un1Ve9x1zLNn4EM50Lia+0EV99NdbK+cn0er7HC7IvBA23mBg3P+8dUkMXy4leL33UQ==
dependencies:
"@types/json-schema" "^7.0.3"
- "@typescript-eslint/scope-manager" "4.15.0"
- "@typescript-eslint/types" "4.15.0"
- "@typescript-eslint/typescript-estree" "4.15.0"
+ "@typescript-eslint/scope-manager" "4.16.1"
+ "@typescript-eslint/types" "4.16.1"
+ "@typescript-eslint/typescript-estree" "4.16.1"
eslint-scope "^5.0.0"
eslint-utils "^2.0.0"
-"@typescript-eslint/[email protected]":
- version "4.15.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.15.0.tgz#8df94365b4b7161f9e8514fe28aef19954810b6b"
- integrity sha512-L6Dtbq8Bc7g2aZwnIBETpmUa9XDKCMzKVwAArnGp5Mn7PRNFjf3mUzq8UeBjL3K8t311hvevnyqXAMSmxO8Gpg==
+"@typescript-eslint/[email protected]":
+ version "4.16.1"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.16.1.tgz#3bbd3234dd3c5b882b2bcd9899bc30e1e1586d2a"
+ integrity sha512-/c0LEZcDL5y8RyI1zLcmZMvJrsR6SM1uetskFkoh3dvqDKVXPsXI+wFB/CbVw7WkEyyTKobC1mUNp/5y6gRvXg==
dependencies:
- "@typescript-eslint/scope-manager" "4.15.0"
- "@typescript-eslint/types" "4.15.0"
- "@typescript-eslint/typescript-estree" "4.15.0"
+ "@typescript-eslint/scope-manager" "4.16.1"
+ "@typescript-eslint/types" "4.16.1"
+ "@typescript-eslint/typescript-estree" "4.16.1"
debug "^4.1.1"
-"@typescript-eslint/[email protected]":
- version "4.15.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.15.0.tgz#c42703558ea6daaaba51a9c3a86f2902dbab9432"
- integrity sha512-CSNBZnCC2jEA/a+pR9Ljh8Y+5TY5qgbPz7ICEk9WCpSEgT6Pi7H2RIjxfrrbUXvotd6ta+i27sssKEH8Azm75g==
+"@typescript-eslint/[email protected]":
+ version "4.16.1"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.16.1.tgz#244e2006bc60cfe46987e9987f4ff49c9e3f00d5"
+ integrity sha512-6IlZv9JaurqV0jkEg923cV49aAn8V6+1H1DRfhRcvZUrptQ+UtSKHb5kwTayzOYTJJ/RsYZdcvhOEKiBLyc0Cw==
dependencies:
- "@typescript-eslint/types" "4.15.0"
- "@typescript-eslint/visitor-keys" "4.15.0"
+ "@typescript-eslint/types" "4.16.1"
+ "@typescript-eslint/visitor-keys" "4.16.1"
-"@typescript-eslint/[email protected]":
- version "4.15.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.15.0.tgz#3011ae1ac3299bb9a5ac56bdd297cccf679d3662"
- integrity sha512-su4RHkJhS+iFwyqyXHcS8EGPlUVoC+XREfy5daivjLur9JP8GhvTmDipuRpcujtGC4M+GYhUOJCPDE3rC5NJrg==
+"@typescript-eslint/[email protected]":
+ version "4.16.1"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.16.1.tgz#5ba2d3e38b1a67420d2487519e193163054d9c15"
+ integrity sha512-nnKqBwMgRlhzmJQF8tnFDZWfunXmJyuXj55xc8Kbfup4PbkzdoDXZvzN8//EiKR27J6vUSU8j4t37yUuYPiLqA==
-"@typescript-eslint/[email protected]":
- version "4.15.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.15.0.tgz#402c86a7d2111c1f7a2513022f22a38a395b7f93"
- integrity sha512-jG6xTmcNbi6xzZq0SdWh7wQ9cMb2pqXaUp6bUZOMsIlu5aOlxGxgE/t6L/gPybybQGvdguajXGkZKSndZJpksA==
+"@typescript-eslint/[email protected]":
+ version "4.16.1"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.16.1.tgz#c2fc46b05a48fbf8bbe8b66a63f0a9ba04b356f1"
+ integrity sha512-m8I/DKHa8YbeHt31T+UGd/l8Kwr0XCTCZL3H4HMvvLCT7HU9V7yYdinTOv1gf/zfqNeDcCgaFH2BMsS8x6NvJg==
dependencies:
- "@typescript-eslint/types" "4.15.0"
- "@typescript-eslint/visitor-keys" "4.15.0"
+ "@typescript-eslint/types" "4.16.1"
+ "@typescript-eslint/visitor-keys" "4.16.1"
debug "^4.1.1"
globby "^11.0.1"
is-glob "^4.0.1"
semver "^7.3.2"
tsutils "^3.17.1"
-"@typescript-eslint/[email protected]":
- version "4.15.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.15.0.tgz#2a07768df30c8a5673f1bce406338a07fdec38ca"
- integrity sha512-RnDtJwOwFucWFAMjG3ghCG/ikImFJFEg20DI7mn4pHEx3vC48lIAoyjhffvfHmErRDboUPC7p9Z2il4CLb7qxA==
+"@typescript-eslint/[email protected]":
+ version "4.16.1"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.16.1.tgz#d7571fb580749fae621520deeb134370bbfc7293"
+ integrity sha512-s/aIP1XcMkEqCNcPQtl60ogUYjSM8FU2mq1O7y5cFf3Xcob1z1iXWNB6cC43Op+NGRTFgGolri6s8z/efA9i1w==
dependencies:
- "@typescript-eslint/types" "4.15.0"
+ "@typescript-eslint/types" "4.16.1"
eslint-visitor-keys "^2.0.0"
"@webassemblyjs/[email protected]":
@@ -4042,13 +4041,13 @@ eslint-visitor-keys@^2.0.0:
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8"
integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==
- version "7.19.0"
- resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.19.0.tgz#6719621b196b5fad72e43387981314e5d0dc3f41"
- integrity sha512-CGlMgJY56JZ9ZSYhJuhow61lMPPjUzWmChFya71Z/jilVos7mR/jPgaEfVGgMBY5DshbKdG8Ezb8FDCHcoMEMg==
+ version "7.21.0"
+ resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.21.0.tgz#4ecd5b8c5b44f5dedc9b8a110b01bbfeb15d1c83"
+ integrity sha512-W2aJbXpMNofUp0ztQaF40fveSsJBjlSCSWpy//gzfTvwC+USs/nceBrKmlJOiM8r1bLwP2EuYkCqArn/6QTIgg==
dependencies:
- "@babel/code-frame" "^7.0.0"
- "@eslint/eslintrc" "^0.3.0"
+ "@babel/code-frame" "7.12.11"
+ "@eslint/eslintrc" "^0.4.0"
ajv "^6.10.0"
chalk "^4.0.0"
cross-spawn "^7.0.2"
@@ -4059,9 +4058,9 @@ [email protected]:
eslint-utils "^2.1.0"
eslint-visitor-keys "^2.0.0"
espree "^7.3.1"
- esquery "^1.2.0"
+ esquery "^1.4.0"
esutils "^2.0.2"
- file-entry-cache "^6.0.0"
+ file-entry-cache "^6.0.1"
functional-red-black-tree "^1.0.1"
glob-parent "^5.0.0"
globals "^12.1.0"
@@ -4099,10 +4098,10 @@ esprima@^4.0.0, esprima@^4.0.1:
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
-esquery@^1.2.0:
- version "1.3.1"
- resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.3.1.tgz#b78b5828aa8e214e29fb74c4d5b752e1c033da57"
- integrity sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==
+esquery@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5"
+ integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==
dependencies:
estraverse "^5.1.0"
@@ -4366,10 +4365,10 @@ fb-watchman@^2.0.0:
dependencies:
bser "2.1.1"
-file-entry-cache@^6.0.0:
- version "6.0.0"
- resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.0.tgz#7921a89c391c6d93efec2169ac6bf300c527ea0a"
- integrity sha512-fqoO76jZ3ZnYrXLDRxBR1YvOvc0k844kcOg40bgsPrE25LAb/PDqTY+ho64Xh2c8ZXgIKldchCFHczG2UVRcWA==
+file-entry-cache@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
+ integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==
dependencies:
flat-cache "^3.0.4"
@@ -4826,10 +4825,10 @@ html-minifier-terser@^5.0.1:
relateurl "^0.2.7"
terser "^4.6.3"
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.0.0.tgz#457a9defb33ce368135078b4e0387a27f3fe244d"
- integrity sha512-kxTyb8cyZwEyUqXTgdHRUOF4C7uCrquzw2T+YTudehm/yspodgCkREjdmc4dXI8k2P4NEjqOVbnOOlPZg4TKJA==
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.2.0.tgz#d675ad0027a89de6b3d9950e0b57656dcfd97fbf"
+ integrity sha512-0wkVlJKq7edCN793gdLgdAm5m196qI2vb5SGXy4AtGOFB/lYKyS10+3Vkhe6Bo0acddAW3QVw+0ysgWoko/IEQ==
dependencies:
"@types/html-minifier-terser" "^5.0.0"
html-minifier-terser "^5.0.1"
@@ -7234,10 +7233,10 @@ [email protected]:
tiny-invariant "^1.0.2"
tiny-warning "^1.0.0"
- version "0.10.4"
- resolved "https://registry.yarnpkg.com/react-spinners/-/react-spinners-0.10.4.tgz#3789086f3c1289d904163517dfa240e13ffd460c"
- integrity sha512-WRzHTHjx1nvMzsyTXg1J8VVDYadGGeas6pzzxGk0T+dVSZpMIN9NrKV/h76SybdsU8cUp55+u9L1V1C9/oafhw==
+ version "0.10.6"
+ resolved "https://registry.yarnpkg.com/react-spinners/-/react-spinners-0.10.6.tgz#7e780144aaf54372231f6168ca075b4cd70e48ef"
+ integrity sha512-UPLcaMFhFnLWtS1zVDDT14ssW08gnZ44cjPN6GL27dEGboiCvRSthOilZXRDWESp449GB2iI6gjEbxzaMtA+dg==
dependencies:
"@emotion/core" "^10.0.35"
@@ -8620,10 +8619,10 @@ typedarray-to-buffer@^3.1.5:
dependencies:
is-typedarray "^1.0.0"
- version "4.1.4"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.4.tgz#f058636e2f4f83f94ddaae07b20fd5e14598432f"
- integrity sha512-+Uru0t8qIRgjuCpiSPpfGuhHecMllk5Zsazj5LZvVsEStEjmIRRBZe+jHjGQvsgS7M1wONy2PQXd67EMyV6acg==
+ version "4.2.3"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.3.tgz#39062d8019912d43726298f09493d598048c1ce3"
+ integrity sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==
unicode-canonical-property-names-ecmascript@^1.0.4:
version "1.0.4"
@@ -8983,10 +8982,10 @@ webpack-sources@^2.1.1, webpack-sources@^2.2.0:
source-list-map "^2.0.1"
source-map "^0.6.1"
- version "5.21.2"
- resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.21.2.tgz#647507e50d3637695be28af58a6a8246050394e7"
- integrity sha512-xHflCenx+AM4uWKX71SWHhxml5aMXdy2tu/vdi4lClm7PADKxlyDAFFN1rEFzNV0MAoPpHtBeJnl/+K6F4QBPg==
+ version "5.23.0"
+ resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.23.0.tgz#9ed57e9a54b267b3549899271ad780cddc6ee316"
+ integrity sha512-RC6dwDuRxiU75F8XC4H08NtzUrMfufw5LDnO8dTtaKU2+fszEdySCgZhNwSBBn516iNaJbQI7T7OPHIgCwcJmg==
dependencies:
"@types/eslint-scope" "^3.7.0"
"@types/estree" "^0.0.46"