diff --git a/site/components/Form/FormCloseButton.stories.tsx b/site/components/Form/FormCloseButton.stories.tsx new file mode 100644 index 0000000000000..6e011a2536d51 --- /dev/null +++ b/site/components/Form/FormCloseButton.stories.tsx @@ -0,0 +1,16 @@ +import { Story } from "@storybook/react" +import React from "react" +import { FormCloseButton, FormCloseButtonProps } from "./FormCloseButton" + +export default { + title: "Form/FormCloseButton", + component: FormCloseButton, + argTypes: { + onClose: { action: "onClose" }, + }, +} + +const Template: Story = (args) => + +export const Example = Template.bind({}) +Example.args = {} diff --git a/site/components/Form/FormCloseButton.test.tsx b/site/components/Form/FormCloseButton.test.tsx new file mode 100644 index 0000000000000..393f7bcc657f0 --- /dev/null +++ b/site/components/Form/FormCloseButton.test.tsx @@ -0,0 +1,70 @@ +import { fireEvent, render, screen } from "@testing-library/react" +import React from "react" +import { FormCloseButton } from "./FormCloseButton" + +describe("FormCloseButton", () => { + it("renders", async () => { + // When + render( + { + return + }} + />, + ) + + // Then + await screen.findByText("ESC") + }) + + it("calls onClose when clicked", async () => { + // Given + const onClose = jest.fn() + + // When + render() + + // Then + const element = await screen.findByText("ESC") + + // When + fireEvent.click(element) + + // Then + expect(onClose).toBeCalledTimes(1) + }) + + it("calls onClose when escape is pressed", async () => { + // Given + const onClose = jest.fn() + + // When + render() + + // Then + const element = await screen.findByText("ESC") + + // When + fireEvent.keyDown(element, { key: "Escape", code: "Esc", charCode: 27 }) + + // Then + expect(onClose).toBeCalledTimes(1) + }) + + it("doesn't call onClose if another key is pressed", async () => { + // Given + const onClose = jest.fn() + + // When + render() + + // Then + const element = await screen.findByText("ESC") + + // When + fireEvent.keyDown(element, { key: "Enter", code: "Enter", charCode: 13 }) + + // Then + expect(onClose).toBeCalledTimes(0) + }) +}) diff --git a/site/components/Form/FormCloseButton.tsx b/site/components/Form/FormCloseButton.tsx new file mode 100644 index 0000000000000..a22f71e8c5b7d --- /dev/null +++ b/site/components/Form/FormCloseButton.tsx @@ -0,0 +1,55 @@ +import IconButton from "@material-ui/core/IconButton" +import { makeStyles } from "@material-ui/core/styles" +import Typography from "@material-ui/core/Typography" +import React, { useEffect } from "react" +import { CloseIcon } from "../Icons/Close" + +export interface FormCloseButtonProps { + onClose: () => void +} + +export const FormCloseButton: React.FC = ({ onClose }) => { + const styles = useStyles() + + useEffect(() => { + const handleKeyPress = (event: KeyboardEvent) => { + if (event.key === "Escape") { + onClose() + } + } + + document.body.addEventListener("keydown", handleKeyPress, false) + + return () => { + document.body.removeEventListener("keydown", handleKeyPress, false) + } + }, []) + + return ( + + + + ESC + + + ) +} + +const useStyles = makeStyles((theme) => ({ + closeButton: { + position: "fixed", + top: theme.spacing(3), + right: theme.spacing(6), + opacity: 0.5, + color: theme.palette.text.primary, + "&:hover": { + opacity: 1, + }, + }, + label: { + position: "absolute", + left: "50%", + top: "100%", + transform: "translate(-50%, 50%)", + }, +})) diff --git a/site/components/Form/index.ts b/site/components/Form/index.ts index 80ddbbac74b3b..08456432c7f74 100644 --- a/site/components/Form/index.ts +++ b/site/components/Form/index.ts @@ -1,3 +1,4 @@ +export * from "./FormCloseButton" export * from "./FormSection" export * from "./FormDropdownField" export * from "./FormTextField" diff --git a/site/components/Icons/Close.tsx b/site/components/Icons/Close.tsx new file mode 100644 index 0000000000000..ab9a44a898941 --- /dev/null +++ b/site/components/Icons/Close.tsx @@ -0,0 +1,8 @@ +import SvgIcon from "@material-ui/core/SvgIcon" +import React from "react" + +export const CloseIcon: typeof SvgIcon = (props) => ( + + + +) diff --git a/site/forms/CreateProjectForm.tsx b/site/forms/CreateProjectForm.tsx index 1a4558e9b2ef8..a930cf487e054 100644 --- a/site/forms/CreateProjectForm.tsx +++ b/site/forms/CreateProjectForm.tsx @@ -4,7 +4,14 @@ import { FormikContextType, useFormik } from "formik" import React from "react" import * as Yup from "yup" -import { DropdownItem, FormDropdownField, FormTextField, FormTitle, FormSection } from "../components/Form" +import { + DropdownItem, + FormDropdownField, + FormTextField, + FormTitle, + FormSection, + FormCloseButton, +} from "../components/Form" import { LoadingButton } from "../components/Button" import { Organization, Project, Provisioner, CreateProjectRequest } from "./../api" @@ -59,6 +66,7 @@ export const CreateProjectForm: React.FC = ({ return (
+ = ({ project, on } /> + +