From dca1263fe95acb96bb5deae36b1393319e2a1515 Mon Sep 17 00:00:00 2001 From: Edward Louth Date: Thu, 2 Nov 2023 11:02:33 +0000 Subject: [PATCH] Start on select on integration (#765) * Start on select on integration * Test fixes * Further work * Updates to buttons * Stepper redesign * Add integration to setup connection * Lint fix * Test updates --- .../components/chat/WebsocketChat.test.tsx | 10 +- .../connectors/ConnectorCard.test.tsx | 26 ++++ .../connections/connectors/ConnectorCard.tsx | 88 +++++++------- .../connections/connectors/ConnectorIcon.tsx | 25 ++++ .../connections/connectors/ConnectorList.tsx | 23 ++-- .../connections/create/ConnectionFile.tsx | 32 ++--- .../create/ConnectorCategoryTabs.tsx | 30 +++++ .../create/ConnectorComingSoon.tsx | 35 ++---- .../connections/create/ConnectorSearch.tsx | 29 +++++ .../create/ConnectorSelect.test.tsx | 42 ++++++- .../connections/create/ConnectorSelect.tsx | 89 ++++++-------- .../connections/create/ConnectorSelectTab.tsx | 25 ++-- .../create/CreateConnectionWizard.test.tsx | 36 ++---- .../create/CreateConnectionWizard.tsx | 14 ++- .../connections/create/SetSchedule.tsx | 31 +++-- .../create/SetupConnection.test.tsx | 64 +++++++--- .../create/SetupConnectionForm.tsx | 71 ++++++++--- .../src/components/wizards/WizardAppBar.tsx | 44 ++----- .../components/wizards/WizardBottomBar.tsx | 70 +++-------- .../src/components/wizards/WizardLayout.tsx | 20 +--- .../src/components/wizards/WizardStepper.tsx | 112 +++++++----------- .../wizards/WizardSubtitle.test.tsx | 12 +- .../src/components/wizards/WizardSubtitle.tsx | 64 ++++------ .../connections/ConnectionCreate.test.tsx | 10 +- 24 files changed, 539 insertions(+), 463 deletions(-) create mode 100644 grai-frontend/src/components/connections/connectors/ConnectorIcon.tsx create mode 100644 grai-frontend/src/components/connections/create/ConnectorCategoryTabs.tsx create mode 100644 grai-frontend/src/components/connections/create/ConnectorSearch.tsx diff --git a/grai-frontend/src/components/chat/WebsocketChat.test.tsx b/grai-frontend/src/components/chat/WebsocketChat.test.tsx index dd28a62f4..365053ab9 100644 --- a/grai-frontend/src/components/chat/WebsocketChat.test.tsx +++ b/grai-frontend/src/components/chat/WebsocketChat.test.tsx @@ -32,15 +32,15 @@ test("type", async () => { expect(screen.getByRole("textbox")).toBeInTheDocument() - user.type(screen.getByRole("textbox"), "He") + user.type(screen.getByRole("textbox"), "H") - await waitFor(() => expect(screen.getByRole("textbox")).toHaveValue("He")) + await waitFor(() => expect(screen.getByRole("textbox")).toHaveValue("H")) user.type(screen.getByRole("textbox"), "{enter}") await waitFor(() => expect(screen.getByRole("textbox")).toHaveValue("")) - expect(screen.getByText("He")).toBeInTheDocument() + expect(screen.getByText("H")).toBeInTheDocument() }) test("receive", async () => { @@ -48,7 +48,7 @@ test("receive", async () => { await server.connected - await act(async () => server.send(JSON.stringify({ message: "He" }))) + await act(async () => server.send(JSON.stringify({ message: "H" }))) - await screen.findByText("He") + await screen.findByText("H") }) diff --git a/grai-frontend/src/components/connections/connectors/ConnectorCard.test.tsx b/grai-frontend/src/components/connections/connectors/ConnectorCard.test.tsx index cabe9205d..169e8e01b 100644 --- a/grai-frontend/src/components/connections/connectors/ConnectorCard.test.tsx +++ b/grai-frontend/src/components/connections/connectors/ConnectorCard.test.tsx @@ -12,6 +12,7 @@ test("renders", async () => { id: "1", name: "connector 1", metadata: null, + icon: null, }} onSelect={onSelect} />, @@ -29,6 +30,7 @@ test("coming soon", async () => { name: "connector 1", metadata: null, status: "coming_soon", + icon: null, }} onSelect={onSelect} />, @@ -36,6 +38,28 @@ test("coming soon", async () => { withRouter: true, }, ) + + expect(screen.getByText(/Coming soon/i)).toBeInTheDocument() +}) + +test("alpha", async () => { + render( + {}} + />, + { + withRouter: true, + }, + ) + + expect(screen.getByText(/alpha/i)).toBeInTheDocument() }) test("click", async () => { @@ -47,6 +71,7 @@ test("click", async () => { id: "1", name: "connector 1", metadata: null, + icon: null, }} onSelect={onSelect} />, @@ -72,6 +97,7 @@ test("to", async () => { name: "connector 1", metadata: null, to: "a", + icon: null, }} onSelect={onSelect} />, diff --git a/grai-frontend/src/components/connections/connectors/ConnectorCard.tsx b/grai-frontend/src/components/connections/connectors/ConnectorCard.tsx index 398accead..6f5e37d49 100644 --- a/grai-frontend/src/components/connections/connectors/ConnectorCard.tsx +++ b/grai-frontend/src/components/connections/connectors/ConnectorCard.tsx @@ -10,11 +10,12 @@ import { Typography, } from "@mui/material" import { useNavigate } from "react-router-dom" +import ConnectorIcon from "./ConnectorIcon" export interface Connector { id: string name: string - icon?: string | null | ReactNode + icon: string | null | ReactNode status?: string metadata: any to?: string @@ -31,9 +32,18 @@ const ConnectorCard: React.FC = ({ }) => { const navigate = useNavigate() + const comingSoon = connector.status === "coming_soon" + return ( - + { if (connector.to) { @@ -42,59 +52,55 @@ const ConnectorCard: React.FC = ({ } onSelect(connector) }} - className="connector-card" + sx={{ height: "100%" }} > - - - {connector.icon && - (typeof connector.icon === "string" ? ( - {`${connector.name} - ) : ( - connector.icon - ))} + + + {connector.icon && } {connector.name} + + {connector.name} + } /> - {connector.status !== "general_release" && ( - theme.palette.grey[100], - }} - > - theme.palette.grey[500], - textTransform: "uppercase", + position: "relative", + top: -105, + right: comingSoon ? -220 : -275, + width: "fit-content", + textAlign: "center", + px: "12px", + py: "6px", + borderRadius: "100px", + background: "#f4edff", }} > - {connector.status?.replace("_", " ")} - - - )} + + {connector.status?.replace("_", " ")} + + + )} ) } diff --git a/grai-frontend/src/components/connections/connectors/ConnectorIcon.tsx b/grai-frontend/src/components/connections/connectors/ConnectorIcon.tsx new file mode 100644 index 000000000..b0b339688 --- /dev/null +++ b/grai-frontend/src/components/connections/connectors/ConnectorIcon.tsx @@ -0,0 +1,25 @@ +import React, { ReactNode } from "react" + +export interface Connector { + icon: string | ReactNode + name: string +} + +type ConnectorIconProps = { + connector: Connector +} + +const ConnectorIcon: React.FC = ({ connector }) => { + if (typeof connector.icon === "string") + return ( + {`${connector.name} + ) + + return <>{connector.icon} +} + +export default ConnectorIcon diff --git a/grai-frontend/src/components/connections/connectors/ConnectorList.tsx b/grai-frontend/src/components/connections/connectors/ConnectorList.tsx index 8b379124a..a5314c12d 100644 --- a/grai-frontend/src/components/connections/connectors/ConnectorList.tsx +++ b/grai-frontend/src/components/connections/connectors/ConnectorList.tsx @@ -1,30 +1,23 @@ import React from "react" -import { Typography, Grid } from "@mui/material" +import { Grid } from "@mui/material" import ConnectorCard, { Connector } from "./ConnectorCard" type ConnectorListProps = { - title: string connectors: Connector[] onSelect: (connector: Connector) => void } const ConnectorList: React.FC = ({ - title, connectors, onSelect, }) => ( - <> - - {title} - - - {connectors.map(connector => ( - - - - ))} - - + + {connectors.map(connector => ( + + + + ))} + ) export default ConnectorList diff --git a/grai-frontend/src/components/connections/create/ConnectionFile.tsx b/grai-frontend/src/components/connections/create/ConnectionFile.tsx index 212e5c1ca..3531cfa8d 100644 --- a/grai-frontend/src/components/connections/create/ConnectionFile.tsx +++ b/grai-frontend/src/components/connections/create/ConnectionFile.tsx @@ -103,7 +103,7 @@ const ConnectionFile: React.FC = ({ }, }) .then(res => - workspaceNavigate(`runs/${res.data?.uploadConnectorFile.id}`) + workspaceNavigate(`runs/${res.data?.uploadConnectorFile.id}`), ) .then(() => enqueueSnackbar("File uploaded")) .catch(() => {}) @@ -120,8 +120,8 @@ const ConnectionFile: React.FC = ({ return (
@@ -154,22 +154,26 @@ const ConnectionFile: React.FC = ({ onChange={file => setValues({ ...values, file })} accept={accept} /> + + + Finish + + - - - Finish - - ) } diff --git a/grai-frontend/src/components/connections/create/ConnectorCategoryTabs.tsx b/grai-frontend/src/components/connections/create/ConnectorCategoryTabs.tsx new file mode 100644 index 000000000..54c5f3fc4 --- /dev/null +++ b/grai-frontend/src/components/connections/create/ConnectorCategoryTabs.tsx @@ -0,0 +1,30 @@ +import React from "react" +import { Tab, Tabs } from "@mui/material" + +type ConnectorCategoryTabsProps = { + categories: string[] + value: string | null + onChange: (value: string | null) => void +} + +const ConnectorCategoryTabs: React.FC = ({ + categories, + value, + onChange, +}) => { + return ( + onChange(newValue)}> + + {categories.map(category => ( + + ))} + + ) +} + +export default ConnectorCategoryTabs diff --git a/grai-frontend/src/components/connections/create/ConnectorComingSoon.tsx b/grai-frontend/src/components/connections/create/ConnectorComingSoon.tsx index 55a8fa518..ebf8c42b8 100644 --- a/grai-frontend/src/components/connections/create/ConnectorComingSoon.tsx +++ b/grai-frontend/src/components/connections/create/ConnectorComingSoon.tsx @@ -1,8 +1,6 @@ import React from "react" -import { Card, Typography } from "@mui/material" -import WizardBottomBar from "components/wizards/WizardBottomBar" +import { Box, Button, Typography } from "@mui/material" import { ElementOptions } from "components/wizards/WizardLayout" -import WizardSubtitle from "components/wizards/WizardSubtitle" import { ConnectorType } from "../ConnectionsForm" type ConnectorComingSoonProps = { @@ -14,26 +12,17 @@ const ConnectorComingSoon: React.FC = ({ connector, opts, }) => ( - <> - - - - {connector.name} coming soon - - We would love to build this integration for you, -
- just get in touch on Slack. -
-
- - + + {connector.name} coming soon + + We would love to build this integration for you, +
+ just get in touch on Slack. +
+ +
) export default ConnectorComingSoon diff --git a/grai-frontend/src/components/connections/create/ConnectorSearch.tsx b/grai-frontend/src/components/connections/create/ConnectorSearch.tsx new file mode 100644 index 000000000..57ff3b00c --- /dev/null +++ b/grai-frontend/src/components/connections/create/ConnectorSearch.tsx @@ -0,0 +1,29 @@ +import React from "react" +import { Search } from "@mui/icons-material" +import { InputAdornment, TextField } from "@mui/material" + +type ConnectorSearchProps = { + value: string + onChange: (value: string) => void +} + +const ConnectorSearch: React.FC = ({ + value, + onChange, +}) => ( + onChange(event.target.value)} + placeholder="Search" + size="small" + InputProps={{ + endAdornment: ( + + + + ), + }} + /> +) + +export default ConnectorSearch diff --git a/grai-frontend/src/components/connections/create/ConnectorSelect.test.tsx b/grai-frontend/src/components/connections/create/ConnectorSelect.test.tsx index 51855d43c..317cc34e2 100644 --- a/grai-frontend/src/components/connections/create/ConnectorSelect.test.tsx +++ b/grai-frontend/src/components/connections/create/ConnectorSelect.test.tsx @@ -1,6 +1,7 @@ import React from "react" +import userEvent from "@testing-library/user-event" import { GraphQLError } from "graphql" -import { render, waitFor, screen } from "testing" +import { render, waitFor, screen, act } from "testing" import ConnectorSelect, { GET_CONNECTORS } from "./ConnectorSelect" const onSelect = jest.fn() @@ -73,7 +74,7 @@ test("renders other", async () => { render(, { mocks, withRouter: true }) await waitFor(() => { - expect(screen.getByText("other")).toBeInTheDocument() + expect(screen.getByText("others")).toBeInTheDocument() }) await waitFor(() => { @@ -99,3 +100,40 @@ test("error", async () => { expect(screen.getByText("Error!")).toBeInTheDocument() }) }) + +test("search", async () => { + const user = userEvent.setup() + + render( {}} />, { withRouter: true }) + + await waitFor(() => { + expect(screen.getAllByText("Hello World")).toBeTruthy() + }) + + await act( + async () => await user.type(screen.getByRole("textbox"), "PostgreSQL"), + ) + + await waitFor(() => { + expect(screen.getAllByText("Hello World")).toBeTruthy() + }) +}) + +test("filter", async () => { + const user = userEvent.setup() + + render( {}} />, { withRouter: true }) + + await waitFor(() => { + expect(screen.getAllByText("Hello World")).toBeTruthy() + }) + + await act( + async () => + await user.click(screen.getByRole("tab", { name: /hello world/i })), + ) + + await waitFor(() => { + expect(screen.getAllByText("Hello World")).toBeTruthy() + }) +}) diff --git a/grai-frontend/src/components/connections/create/ConnectorSelect.tsx b/grai-frontend/src/components/connections/create/ConnectorSelect.tsx index f9e391aaf..87c0f527a 100644 --- a/grai-frontend/src/components/connections/create/ConnectorSelect.tsx +++ b/grai-frontend/src/components/connections/create/ConnectorSelect.tsx @@ -1,4 +1,4 @@ -import React from "react" +import React, { useState } from "react" import { gql, useQuery } from "@apollo/client" import { Box } from "@mui/material" import useWorkspace from "helpers/useWorkspace" @@ -6,6 +6,8 @@ import { Connections } from "components/icons" import Loading from "components/layout/Loading" import GraphError from "components/utils/GraphError" import { GetConnectors } from "./__generated__/GetConnectors" +import ConnectorCategoryTabs from "./ConnectorCategoryTabs" +import ConnectorSearch from "./ConnectorSearch" import { Connector } from "../connectors/ConnectorCard" import ConnectorList from "../connectors/ConnectorList" @@ -22,47 +24,30 @@ export const GET_CONNECTORS = gql` } ` -type Category = { - title: string - connectors: Connector[] -} - type ConnectorSelectProps = { onSelect: (connector: Connector) => void } const ConnectorSelect: React.FC = ({ onSelect }) => { + const [search, setSearch] = useState("") + const [category, setCategory] = useState(null) + const { routePrefix } = useWorkspace() const { loading, error, data } = useQuery(GET_CONNECTORS) if (error) return - if (loading) return - - const categories = data?.connectors.reduce((res, connector) => { - const category = connector.category ?? "other" - - const group = res.find(g => g.title === category) + if (loading || !data) return - if (group) { - group.connectors.push(connector) - } else { - const newCategory = { - title: category, - connectors: [connector], - } + const categories = data.connectors.reduce((res, connector) => { + const category = connector.category ?? "others" - return res.concat(newCategory) + if (!res.includes(category)) { + return res.concat(category) } return res }, []) - const databases = categories?.find(c => c.title === "databases") - const datatools = categories?.find(c => c.title === "data tools") - const others = categories?.filter( - c => !["databases", "data tools"].includes(c.title), - ) - const emptySource = { id: "source", name: "Empty Source", @@ -73,36 +58,36 @@ const ConnectorSelect: React.FC = ({ onSelect }) => { ), to: `${routePrefix}/sources/create`, + category: "others", } + const connectors = [...(data?.connectors ?? []), emptySource] + const filteredConnectors = category + ? connectors.filter( + connector => (connector.category ?? "others") === category, + ) + : connectors + const searchedConnectors = search + ? filteredConnectors.filter(connector => + connector.name.toLowerCase().includes(search.toLowerCase()), + ) + : filteredConnectors + return ( <> - {databases && ( - - )} - {datatools && ( - - )} - {others?.map(category => ( - - ))} + + + + + + + + + ) } diff --git a/grai-frontend/src/components/connections/create/ConnectorSelectTab.tsx b/grai-frontend/src/components/connections/create/ConnectorSelectTab.tsx index f61207c97..8bf9e51f8 100644 --- a/grai-frontend/src/components/connections/create/ConnectorSelectTab.tsx +++ b/grai-frontend/src/components/connections/create/ConnectorSelectTab.tsx @@ -1,7 +1,6 @@ import React from "react" -import WizardBottomBar from "components/wizards/WizardBottomBar" +import { Box, Typography } from "@mui/material" import { ElementOptions } from "components/wizards/WizardLayout" -import WizardSubtitle from "components/wizards/WizardSubtitle" import ConnectorSelect from "./ConnectorSelect" import { Connector } from "../connectors/ConnectorCard" @@ -11,16 +10,26 @@ type ConnectorSelectTabProps = { } const ConnectorSelectTab: React.FC = ({ - opts, onSelect, }) => ( <> - + + + Select integration + + Choose the integration for your workflow + - ) diff --git a/grai-frontend/src/components/connections/create/CreateConnectionWizard.test.tsx b/grai-frontend/src/components/connections/create/CreateConnectionWizard.test.tsx index 9a42c9279..334da02ca 100644 --- a/grai-frontend/src/components/connections/create/CreateConnectionWizard.test.tsx +++ b/grai-frontend/src/components/connections/create/CreateConnectionWizard.test.tsx @@ -18,31 +18,15 @@ test("renders", async () => { withRouter: true, }) - expect(screen.getByText("Select an integration")).toBeInTheDocument() + expect( + screen.getByRole("heading", { name: /Select integration/i }), + ).toBeInTheDocument() await waitFor(() => { expect(screen.getAllByText("Hello World")).toBeTruthy() }) }) -test("close", async () => { - const user = userEvent.setup() - - render(, { - routes: ["/:organisationName/:workspaceName/sources"], - }) - - expect(screen.getByText("Select an integration")).toBeInTheDocument() - - await act(async () => await user.click(screen.getByTestId("CloseIcon"))) - - await waitFor(() => { - expect(screen.queryByText("Select an integration")).toBeFalsy() - }) - - expect(screen.getByText("New Page")).toBeInTheDocument() -}) - const connectorsMock = { request: { query: GET_CONNECTORS, @@ -90,7 +74,9 @@ const connectorsMock = { } const submit = async (user: UserEvent, container: HTMLElement) => { - expect(screen.getByText("Select an integration")).toBeInTheDocument() + expect( + screen.getByRole("heading", { name: /Select integration/i }), + ).toBeInTheDocument() await waitFor(() => { expect(screen.getByRole("button", { name: /PostgreSQL/i })).toBeTruthy() @@ -101,11 +87,13 @@ const submit = async (user: UserEvent, container: HTMLElement) => { await user.click(screen.getByRole("button", { name: /PostgreSQL/i })), ) - await waitFor(() => { - expect(screen.queryByText("Select an integration")).toBeFalsy() - }) + expect( + screen.queryByRole("heading", { name: /Select integration/i }), + ).not.toBeInTheDocument() - expect(screen.getByText("Connect to PostgreSQL")).toBeInTheDocument() + expect( + screen.getByRole("heading", { name: /Setup connection/i }), + ).toBeInTheDocument() await act( async () => diff --git a/grai-frontend/src/components/connections/create/CreateConnectionWizard.tsx b/grai-frontend/src/components/connections/create/CreateConnectionWizard.tsx index 435f249d2..a7922cf37 100644 --- a/grai-frontend/src/components/connections/create/CreateConnectionWizard.tsx +++ b/grai-frontend/src/components/connections/create/CreateConnectionWizard.tsx @@ -1,5 +1,7 @@ import React, { useState } from "react" import useWorkspace from "helpers/useWorkspace" +import PageContent from "components/layout/PageContent" +import PageHeader from "components/layout/PageHeader" import WizardLayout, { WizardSteps } from "components/wizards/WizardLayout" import ConnectorSelectTab from "./ConnectorSelectTab" import SetSchedule from "./SetSchedule" @@ -68,12 +70,12 @@ const CreateConnectionWizard: React.FC = ({ ] return ( - workspaceNavigate("sources")} - className="create-connection-wizard" - /> + <> + + + + + ) } diff --git a/grai-frontend/src/components/connections/create/SetSchedule.tsx b/grai-frontend/src/components/connections/create/SetSchedule.tsx index 6d7336769..da31ed791 100644 --- a/grai-frontend/src/components/connections/create/SetSchedule.tsx +++ b/grai-frontend/src/components/connections/create/SetSchedule.tsx @@ -81,7 +81,10 @@ const SetSchedule: React.FC = ({ return ( <> - + {error && } @@ -147,22 +150,26 @@ const SetSchedule: React.FC = ({ {values?.type === "cron" && ( )} + + + Finish + + - - - Finish - - ) } diff --git a/grai-frontend/src/components/connections/create/SetupConnection.test.tsx b/grai-frontend/src/components/connections/create/SetupConnection.test.tsx index 825aa791e..5723130b6 100644 --- a/grai-frontend/src/components/connections/create/SetupConnection.test.tsx +++ b/grai-frontend/src/components/connections/create/SetupConnection.test.tsx @@ -21,12 +21,19 @@ const opts = { }, } +const connector = { + id: "1", + name: "Test Connector", + metadata: null, + icon: "icon", +} + test("renders", async () => { render( , @@ -35,7 +42,9 @@ test("renders", async () => { }, ) - expect(screen.getByText("Connect to Test Connector")).toBeInTheDocument() + expect( + screen.getByRole("heading", { name: /Setup connection/i }), + ).toBeInTheDocument() }) test("submit", async () => { @@ -45,7 +54,7 @@ test("submit", async () => { , @@ -54,7 +63,9 @@ test("submit", async () => { }, ) - expect(screen.getByText("Connect to Test Connector")).toBeInTheDocument() + expect( + screen.getByRole("heading", { name: /Setup connection/i }), + ).toBeInTheDocument() await act( async () => @@ -122,7 +133,7 @@ test("submit run error", async () => { , @@ -132,7 +143,9 @@ test("submit run error", async () => { }, ) - expect(screen.getByText("Connect to Test Connector")).toBeInTheDocument() + expect( + screen.getByRole("heading", { name: /Setup connection/i }), + ).toBeInTheDocument() await act( async () => @@ -151,7 +164,7 @@ test("submit update", async () => { { }, ) - expect(screen.getByText("Connect to Test Connector")).toBeInTheDocument() + expect( + screen.getByRole("heading", { name: /Setup connection/i }), + ).toBeInTheDocument() await act( async () => @@ -210,7 +225,7 @@ test("submit update error", async () => { { }, ) - expect(screen.getByText("Connect to Test Connector")).toBeInTheDocument() + expect( + screen.getByRole("heading", { name: /Setup connection/i }), + ).toBeInTheDocument() await act( async () => @@ -253,6 +270,7 @@ test("renders file", async () => { extension: "json", }, }, + icon: null, }} connection={null} setConnection={setConnection} @@ -262,7 +280,9 @@ test("renders file", async () => { }, ) - expect(screen.getByText("Connect to Test File Connector")).toBeInTheDocument() + expect( + screen.getByRole("heading", { name: /Setup connection/i }), + ).toBeInTheDocument() }) test("renders file yaml", async () => { @@ -279,6 +299,7 @@ test("renders file yaml", async () => { extension: "yaml", }, }, + icon: null, }} connection={null} setConnection={setConnection} @@ -288,7 +309,9 @@ test("renders file yaml", async () => { }, ) - expect(screen.getByText("Connect to Test YAML Connector")).toBeInTheDocument() + expect( + screen.getByRole("heading", { name: /Setup connection/i }), + ).toBeInTheDocument() }) test("upload file", async () => { @@ -307,6 +330,7 @@ test("upload file", async () => { extension: "json", }, }, + icon: null, }} connection={null} setConnection={setConnection} @@ -316,7 +340,9 @@ test("upload file", async () => { }, ) - expect(screen.getByText("Connect to Test File Connector")).toBeInTheDocument() + expect( + screen.getByRole("heading", { name: /Setup connection/i }), + ).toBeInTheDocument() window.URL.createObjectURL = jest.fn().mockImplementation(() => "url") @@ -368,6 +394,7 @@ test("upload wrong file", async () => { extension: "json", }, }, + icon: null, }} connection={null} setConnection={setConnection} @@ -377,7 +404,9 @@ test("upload wrong file", async () => { }, ) - expect(screen.getByText("Connect to Test File Connector")).toBeInTheDocument() + expect( + screen.getByRole("heading", { name: /Setup connection/i }), + ).toBeInTheDocument() window.URL.createObjectURL = jest.fn().mockImplementation(() => "url") @@ -432,6 +461,7 @@ test("upload file error", async () => { extension: "json", }, }, + icon: null, }} connection={null} setConnection={setConnection} @@ -439,7 +469,9 @@ test("upload file error", async () => { { withRouter: true, mocks }, ) - expect(screen.getByText("Connect to Test File Connector")).toBeInTheDocument() + expect( + screen.getByRole("heading", { name: /Setup connection/i }), + ).toBeInTheDocument() window.URL.createObjectURL = jest.fn().mockImplementation(() => "url") @@ -479,6 +511,7 @@ test("renders coming soon", async () => { name: "Test", metadata: {}, status: "coming_soon", + icon: null, }} connection={null} setConnection={setConnection} @@ -488,6 +521,5 @@ test("renders coming soon", async () => { }, ) - expect(screen.getByText("Test Integration")).toBeInTheDocument() expect(screen.getByText("Test coming soon")).toBeInTheDocument() }) diff --git a/grai-frontend/src/components/connections/create/SetupConnectionForm.tsx b/grai-frontend/src/components/connections/create/SetupConnectionForm.tsx index a2c77abb5..46d139a4b 100644 --- a/grai-frontend/src/components/connections/create/SetupConnectionForm.tsx +++ b/grai-frontend/src/components/connections/create/SetupConnectionForm.tsx @@ -2,7 +2,7 @@ import React, { useState } from "react" import { gql, useMutation } from "@apollo/client" import { ArrowForward } from "@mui/icons-material" import { LoadingButton } from "@mui/lab" -import { Grid, TextField } from "@mui/material" +import { Box, Grid, InputAdornment, TextField } from "@mui/material" import Form from "components/form/Form" import { NewSource } from "components/sources/__generated__/NewSource" import GraphError from "components/utils/GraphError" @@ -23,6 +23,7 @@ import CreateConnectionHelp from "./CreateConnectionHelp" import { Connection } from "./CreateConnectionWizard" import ConnectionsMetadata from "../ConnectionsMetadata" import { Connector } from "../connectors/ConnectorCard" +import ConnectorIcon from "../connectors/ConnectorIcon" export const CREATE_CONNECTION = gql` mutation CreateConnection( @@ -311,13 +312,49 @@ const SetupConnectionForm: React.FC = ({ return (
{errorCreate && } {errorUpdate && } + + + + + + ) : undefined, + sx: { + pt: "23px", + pb: "18px", + pl: "18px", + }, + }} + /> = ({ /> )} {children} + + } + loading={loadingCreate || loadingUpdate} + disabled={!dirty && disabled} + > + Continue + + - - } - loading={loadingCreate || loadingUpdate} - disabled={!dirty && disabled} - > - Continue - - ) } diff --git a/grai-frontend/src/components/wizards/WizardAppBar.tsx b/grai-frontend/src/components/wizards/WizardAppBar.tsx index 447ec4307..a3f37d0fd 100644 --- a/grai-frontend/src/components/wizards/WizardAppBar.tsx +++ b/grai-frontend/src/components/wizards/WizardAppBar.tsx @@ -1,48 +1,20 @@ import React from "react" -import { Close } from "@mui/icons-material" -import { AppBar, Box, IconButton, Toolbar, Typography } from "@mui/material" +import { Box, Divider } from "@mui/material" import { WizardSteps } from "./WizardLayout" import WizardStepper from "./WizardStepper" type WizardAppBarProps = { - title: string steps: WizardSteps activeStep: number - onClose: () => void } -const WizardAppBar: React.FC = ({ - title, - steps, - activeStep, - onClose, -}) => ( - theme.zIndex.drawer + 1, - backgroundColor: "white", - }} - > - - - {title} - - - - - - - - - - - +const WizardAppBar: React.FC = ({ steps, activeStep }) => ( + <> + + + + + ) export default WizardAppBar diff --git a/grai-frontend/src/components/wizards/WizardBottomBar.tsx b/grai-frontend/src/components/wizards/WizardBottomBar.tsx index 13e4d9ba8..e76572d0a 100644 --- a/grai-frontend/src/components/wizards/WizardBottomBar.tsx +++ b/grai-frontend/src/components/wizards/WizardBottomBar.tsx @@ -1,73 +1,31 @@ import React, { ReactNode } from "react" import { ArrowBack } from "@mui/icons-material" -import { - AppBar, - Toolbar, - Container, - Typography, - Box, - Button, -} from "@mui/material" +import { Box, Button } from "@mui/material" import { ElementOptions } from "./WizardLayout" type WizardBottomBarProps = { - actionText?: string opts: ElementOptions children?: ReactNode } const WizardBottomBar: React.FC = ({ - actionText, opts, children, }) => ( - <> - - - - - - {opts.activeStep > 0 && ( - - )} - - - {actionText && ( - theme.palette.grey[500], - fontWeight: 500, - }} - > - {actionText} - - )} + + {opts.activeStep > 0 && ( + + )} - {children} - - - - - + {children} + ) export default WizardBottomBar diff --git a/grai-frontend/src/components/wizards/WizardLayout.tsx b/grai-frontend/src/components/wizards/WizardLayout.tsx index d07dacb0b..28e693abf 100644 --- a/grai-frontend/src/components/wizards/WizardLayout.tsx +++ b/grai-frontend/src/components/wizards/WizardLayout.tsx @@ -1,5 +1,5 @@ import React, { ReactNode, useState } from "react" -import { Box, Container, Toolbar } from "@mui/material" +import { Box, Toolbar } from "@mui/material" import WizardAppBar from "./WizardAppBar" export type ElementOptions = { @@ -17,18 +17,11 @@ export type WizardStep = { export type WizardSteps = WizardStep[] type WizardLayoutProps = { - title: string steps: WizardSteps - onClose: () => void className?: string } -const WizardLayout: React.FC = ({ - title, - steps, - onClose, - className, -}) => { +const WizardLayout: React.FC = ({ steps, className }) => { const [activeStep, setActiveStep] = useState(0) const forwardStep = () => setActiveStep(activeStep + 1) @@ -48,14 +41,9 @@ const WizardLayout: React.FC = ({ return ( - + - {stepElement} + {stepElement} ) } diff --git a/grai-frontend/src/components/wizards/WizardStepper.tsx b/grai-frontend/src/components/wizards/WizardStepper.tsx index e17929338..ae84a6bd8 100644 --- a/grai-frontend/src/components/wizards/WizardStepper.tsx +++ b/grai-frontend/src/components/wizards/WizardStepper.tsx @@ -1,81 +1,35 @@ import React from "react" -import { CheckCircle } from "@mui/icons-material" +import { CheckCircle, ChevronRight } from "@mui/icons-material" import { Stepper, Step, StepLabel, - StepConnector, - stepConnectorClasses, StepIconProps, - styled, SxProps, Theme, + Box, } from "@mui/material" import { WizardSteps } from "./WizardLayout" -const QontoConnector = styled(StepConnector)(({ theme }) => ({ - [`&.${stepConnectorClasses.alternativeLabel}`]: { - top: 10, - left: "calc(-50% + 16px)", - right: "calc(50% + 16px)", - }, - [`&.${stepConnectorClasses.active}`]: { - [`& .${stepConnectorClasses.line}`]: { - borderColor: theme.palette.secondary.main, - }, - }, - [`&.${stepConnectorClasses.completed}`]: { - [`& .${stepConnectorClasses.line}`]: { - borderColor: theme.palette.secondary.main, - }, - }, - [`& .${stepConnectorClasses.line}`]: { - borderColor: - theme.palette.mode === "dark" ? theme.palette.grey[800] : "#eaeaf0", - borderTopWidth: 1, - borderRadius: 1, - marginLeft: 10, - marginRight: 10, - }, -})) +function StepIcon(props: StepIconProps) { + const { active, completed } = props -const QontoStepIconRoot = styled("div")<{ ownerState: { active?: boolean } }>( - ({ theme, ownerState }) => ({ - color: theme.palette.mode === "dark" ? theme.palette.grey[700] : "#eaeaf0", - display: "flex", - height: 22, - alignItems: "center", - ...(ownerState.active && { - color: theme.palette.secondary.main, - }), - "& .QontoStepIcon-completedIcon": { - color: theme.palette.secondary.main, - zIndex: 1, - fontSize: 18, - }, - "& .QontoStepIcon-circle": { - width: 14, - height: 14, - borderRadius: "50%", - backgroundColor: ownerState.active ? "currentColor" : "white", - borderColor: theme.palette.secondary.main, - borderWidth: 1, - borderStyle: "solid", - }, - }) -) + if (completed) return -function QontoStepIcon(props: StepIconProps) { - const { active, completed, className } = props + const borderColor = active ? "#8338EC" : "#EBEBEB" return ( - - {completed ? ( - - ) : ( -
- )} - + ) } @@ -93,10 +47,34 @@ const WizardStepper: React.FC = ({ const stepTitles = steps.map(step => step.title) return ( - } sx={sx}> - {stepTitles.map(step => ( - - {step} + } + sx={{ + ...sx, + // "& .MuiStep-root": { + // border: "1px solid #EBEBEB", + // }, + // "& .MuiStep-root.Mui-active": { + // border: "1px solid #8338EC", + // }, + // "& .MuiStep-root.Mui-completed": { + // border: "1px solid #8338EC", + // }, + }} + > + {stepTitles.map((step, index) => ( + = index ? "1px solid #8338EC" : "1px solid #EBEBEB", + p: "8px", + pr: "16px", + }} + > + {step} ))} diff --git a/grai-frontend/src/components/wizards/WizardSubtitle.test.tsx b/grai-frontend/src/components/wizards/WizardSubtitle.test.tsx index 58a378843..11e3d6956 100644 --- a/grai-frontend/src/components/wizards/WizardSubtitle.test.tsx +++ b/grai-frontend/src/components/wizards/WizardSubtitle.test.tsx @@ -1,5 +1,4 @@ import React from "react" -import { Search } from "@mui/icons-material" import { render, screen } from "testing" import WizardSubtitle from "./WizardSubtitle" @@ -13,14 +12,9 @@ test("renders title", async () => { expect(screen.getByText("Test Title")).toBeInTheDocument() }) -test("renders title icon", async () => { - render() - - expect(screen.getByText("Test Title")).toBeInTheDocument() -}) - -test("renders title react icon", async () => { - render(} />) +test("renders title subTitle", async () => { + render() expect(screen.getByText("Test Title")).toBeInTheDocument() + expect(screen.getByText("Test subTitle")).toBeInTheDocument() }) diff --git a/grai-frontend/src/components/wizards/WizardSubtitle.tsx b/grai-frontend/src/components/wizards/WizardSubtitle.tsx index c5b10596c..0a2993fd2 100644 --- a/grai-frontend/src/components/wizards/WizardSubtitle.tsx +++ b/grai-frontend/src/components/wizards/WizardSubtitle.tsx @@ -1,52 +1,28 @@ -import React, { ReactNode } from "react" -import { AppBar, Toolbar, Container, Typography, Box } from "@mui/material" +import React from "react" +import { Typography, Box } from "@mui/material" type WizardSubtitleProps = { title?: string | null - icon?: string | null | ReactNode - children?: ReactNode + subTitle?: string | null } -const WizardSubtitle: React.FC = ({ - title, - icon, - children, -}) => ( - <> - - - theme.palette.grey[100], - height: 80, - }} - > - - { - - {icon && - (typeof icon === "string" ? ( - {`${title} - ) : ( - icon - ))} - {title && ( - - {title} - - )} - - } - {children} - - - - - +const WizardSubtitle: React.FC = ({ title, subTitle }) => ( + + + {title} + + {subTitle} + ) export default WizardSubtitle diff --git a/grai-frontend/src/pages/connections/ConnectionCreate.test.tsx b/grai-frontend/src/pages/connections/ConnectionCreate.test.tsx index c99adaf5b..af0d00fae 100644 --- a/grai-frontend/src/pages/connections/ConnectionCreate.test.tsx +++ b/grai-frontend/src/pages/connections/ConnectionCreate.test.tsx @@ -8,8 +8,14 @@ test("renders", async () => { }) await waitFor(() => { - expect(screen.getByText("Create Source")).toBeInTheDocument() + expect(screen.getByText("Add Source")).toBeInTheDocument() }) - expect(screen.getByText("Select an integration")).toBeInTheDocument() + expect( + screen.getByRole("heading", { name: /Select integration/i }), + ).toBeInTheDocument() + + await waitFor(() => { + expect(screen.getAllByText("Hello World")).toBeTruthy() + }) })