diff options
-rw-r--r-- | package.json | 26 | ||||
-rw-r--r-- | src/api/forms.ts | 3 | ||||
-rw-r--r-- | src/commonStyles.tsx | 10 | ||||
-rw-r--r-- | src/components/ErrorMessage.tsx | 24 | ||||
-rw-r--r-- | src/components/InputTypes/Radio.tsx | 5 | ||||
-rw-r--r-- | src/components/InputTypes/Range.tsx | 6 | ||||
-rw-r--r-- | src/components/InputTypes/Select.tsx | 18 | ||||
-rw-r--r-- | src/components/InputTypes/ShortText.tsx | 14 | ||||
-rw-r--r-- | src/components/InputTypes/TextArea.tsx | 14 | ||||
-rw-r--r-- | src/components/InputTypes/index.tsx | 19 | ||||
-rw-r--r-- | src/components/Question.tsx | 112 | ||||
-rw-r--r-- | src/pages/FormPage.tsx | 124 | ||||
-rw-r--r-- | src/tests/components/FormListing.test.tsx | 6 | ||||
-rw-r--r-- | src/tests/pages/LandingPage.test.tsx | 3 | ||||
-rw-r--r-- | yarn.lock | 285 |
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", () => { @@ -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== -"@types/[email protected]": - 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== +"@types/[email protected]": + 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" "*" -"@types/[email protected]": - 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== +"@types/[email protected]": + 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" |