Skip to content

Commit

Permalink
🪟 🎨 New progress/steps tracking for connection creation (#7140)
Browse files Browse the repository at this point in the history
  • Loading branch information
teallarson committed Jun 13, 2023
1 parent 0cf190a commit 7230d28
Show file tree
Hide file tree
Showing 13 changed files with 300 additions and 68 deletions.
111 changes: 57 additions & 54 deletions airbyte-webapp/cypress/e2e/connection/createConnection.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { goToDestinationPage, openDestinationOverview } from "@cy/pages/destinat
import { openSourceOverview } from "@cy/pages/sourcePage";
import { goToSourcePage } from "@cy/pages/sourcePage";
import { WebBackendConnectionRead, DestinationRead, SourceRead } from "@src/core/api/types/AirbyteClient";
import { ConnectionRoutePaths, RoutePaths } from "@src/pages/routePaths";
import { RoutePaths, ConnectionRoutePaths } from "@src/pages/routePaths";
import { requestDeleteConnection, requestDeleteDestination, requestDeleteSource } from "commands/api";
import { appendRandomString, submitButtonClick } from "commands/common";
import { runDbQuery } from "commands/db/db";
Expand All @@ -30,7 +30,7 @@ import {
waitForGetSourcesListRequest,
} from "commands/interceptors";

import * as replicationPage from "pages/connection/connectionFormPageObject";
import * as connectionConfigurationForm from "pages/connection/connectionFormPageObject";
import * as connectionListPage from "pages/connection/connectionListPageObject";
import * as newConnectionPage from "pages/connection/createConnectionPageObject";
import { streamDetails } from "pages/connection/StreamDetailsPageObject";
Expand Down Expand Up @@ -95,7 +95,7 @@ describe("Connection - Create new connection", { testIsolation: false }, () => {
});

it("should redirect to 'New connection' configuration page with stream table'", () => {
newConnectionPage.isAtNewConnectionPage();
newConnectionPage.isAtConnectionConfigurationStep();
});
});
describe("From source page", () => {
Expand All @@ -104,95 +104,98 @@ describe("Connection - Create new connection", { testIsolation: false }, () => {
openSourceOverview(source.name);
cy.get("button").contains("Create a connection").click();
cy.get("button").contains(destination.name).click();
newConnectionPage.isAtNewConnectionPage();
newConnectionPage.isAtConnectionConfigurationStep();
});
it("can use new destination", () => {
// this depends on the source having at least one configured connection already
createNewConnectionViaApi(source, destination);
createNewConnectionViaApi(source, destination).then((connection) => {
connectionId = connection.connectionId;

cy.intercept("/api/v1/destinations/create").as("createDestination");
cy.intercept("/api/v1/destinations/create").as("createDestination");

goToSourcePage();
openSourceOverview(source.name);
cy.get("button").contains("add destination").click();
cy.get("button").contains("add a new destination").click();
cy.location("search").should("eq", `?sourceId=${source.sourceId}&destinationType=new`);
goToSourcePage();
openSourceOverview(source.name);
cy.get("button").contains("add destination").click();
cy.get("button").contains("add a new destination").click();
cy.location("search").should("eq", `?sourceId=${source.sourceId}&destinationType=new`);

// confirm toggling the type
cy.get("label").contains("Select an existing destination").click();
cy.location("search").should("eq", `?sourceId=${source.sourceId}&destinationType=existing`);
// confirm toggling the type
cy.get("label").contains("Select an existing destination").click();
cy.location("search").should("eq", `?sourceId=${source.sourceId}&destinationType=existing`);

cy.get("button").contains(destination.name).should("exist");
cy.get("button").contains(destination.name).should("exist");

cy.get("label").contains("Set up a new destination").click();
cy.location("search").should("eq", `?sourceId=${source.sourceId}&destinationType=new`);
cy.get("label").contains("Set up a new destination").click();
cy.location("search").should("eq", `?sourceId=${source.sourceId}&destinationType=new`);

fillLocalJsonForm(appendRandomString("LocalJSON Cypress"), "/local");
fillLocalJsonForm(appendRandomString("LocalJSON Cypress"), "/local");

cy.get("button").contains("Set up destination").click();
cy.get("button").contains("Set up destination").click();

cy.wait("@createDestination", { timeout: 30000 }).then((interception) => {
const createdDestinationId = interception.response?.body.destinationId;
cy.location("search").should("eq", `?sourceId=${source.sourceId}&destinationId=${createdDestinationId}`);
cy.wait("@createDestination", { timeout: 30000 }).then((interception) => {
const createdDestinationId = interception.response?.body.destinationId;
cy.location("search").should("eq", `?sourceId=${source.sourceId}&destinationId=${createdDestinationId}`);

requestDeleteDestination({ destinationId: createdDestinationId });
requestDeleteDestination({ destinationId: createdDestinationId });
});
requestDeleteConnection({ connectionId: connection.connectionId });
});
});
});
describe.only("From destination page", () => {
describe("From destination page", () => {
it("can use existing source", () => {
goToDestinationPage();
openDestinationOverview(destination.name);
cy.get("button").contains("Create a connection").click();
cy.get("button").contains(source.name).click();
newConnectionPage.isAtNewConnectionPage();
newConnectionPage.isAtConnectionConfigurationStep();
});
it("can use new source", () => {
// this depends on the source having at least one configured connection already
createNewConnectionViaApi(source, destination);
createNewConnectionViaApi(source, destination).then((connection) => {
cy.intercept("/api/v1/sources/create").as("createSource");

cy.intercept("/api/v1/sources/create").as("createSource");
goToDestinationPage();
openDestinationOverview(destination.name);
cy.get("button").contains("add source").click();
cy.get("button").contains("add a new source").click();
cy.location("search").should("eq", `?destinationId=${destination.destinationId}&sourceType=new`);

goToDestinationPage();
openDestinationOverview(destination.name);
cy.get("button").contains("add source").click();
cy.get("button").contains("add a new source").click();
cy.location("search").should("eq", `?destinationId=${destination.destinationId}&sourceType=new`);
// confirm can toggle back and
cy.get("label").contains("Select an existing source").click();
cy.location("search").should("eq", `?destinationId=${destination.destinationId}&sourceType=existing`);

// confirm can toggle back and
cy.get("label").contains("Select an existing source").click();
cy.location("search").should("eq", `?destinationId=${destination.destinationId}&sourceType=existing`);
cy.get("button").contains(source.name).should("exist");

cy.get("button").contains(source.name).should("exist");
cy.get("label").contains("Set up a new source").click();
cy.location("search").should("eq", `?destinationId=${destination.destinationId}&sourceType=new`);

cy.get("label").contains("Set up a new source").click();
cy.location("search").should("eq", `?destinationId=${destination.destinationId}&sourceType=new`);
const testPokeSourceName = appendRandomString("Cypress Test Poke");
fillPokeAPIForm(testPokeSourceName, "ditto");
cy.get("button").contains("Set up source").click();
cy.wait("@createSource", { timeout: 30000 }).then((interception) => {
const createdSourceId = interception.response?.body.sourceId;
newConnectionPage.isAtConnectionConfigurationStep();

const testPokeSourceName = appendRandomString("Cypress Test Poke");
fillPokeAPIForm(testPokeSourceName, "ditto");
cy.get("button").contains("Set up source").click();
cy.wait("@createSource", { timeout: 30000 }).then((interception) => {
const createdSourceId = interception.response?.body.sourceId;
newConnectionPage.isAtNewConnectionPage();
requestDeleteSource({ sourceId: createdSourceId });
});

requestDeleteSource({ sourceId: createdSourceId });
newConnectionPage.isAtConnectionConfigurationStep();
requestDeleteConnection({ connectionId: connection.connectionId });
});

newConnectionPage.isAtNewConnectionPage();
});
});
});

describe("Configuration", () => {
before(() => {
it("should set 'Replication frequency' to 'Manual'", () => {
interceptDiscoverSchemaRequest();

cy.visit(
`/${RoutePaths.Connections}/${ConnectionRoutePaths.ConnectionNew}?sourceId=${source.sourceId}&destinationId=${destination.destinationId}`
`/${RoutePaths.Connections}/${ConnectionRoutePaths.ConnectionNew}/${ConnectionRoutePaths.Configure}?sourceId=${source.sourceId}&destinationId=${destination.destinationId}`
);
newConnectionPage.isAtNewConnectionPage();
});

it("should set 'Replication frequency' to 'Manual'", () => {
replicationPage.selectSchedule("Manual");
waitForDiscoverSchemaRequest();
connectionConfigurationForm.selectSchedule("Manual");
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ export const isNewConnectorTypeSelected = (connectorType: ConnectorType) => {
Route checking
*/
export const isAtNewConnectionPage = () => cy.url().should("include", `/connections/new-connection`);

export const isAtConnectionConfigurationStep = () =>
cy.url().should("include", `/connections/new-connection/configure`);
export const isAtConnectionOverviewPage = (connectionId: string) =>
cy.url().should("include", `connections/${connectionId}/status`);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
line-height: normal;
font-family: fonts.$primary;
font-style: normal;
font-weight: 500;
font-weight: 600;
font-size: 10px;

&.default {
Expand All @@ -37,4 +37,16 @@
background: colors.$blue;
color: colors.$white;
}

&.blue--outline {
border: variables.$border-thick solid colors.$blue;
color: colors.$blue;
background: colors.$white;
}

&.grey--outline {
border: variables.$border-thick solid colors.$grey-300;
color: colors.$grey-300;
background: colors.$white;
}
}
13 changes: 11 additions & 2 deletions airbyte-webapp/src/components/ui/NumberBadge/NumberBadge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,23 @@ import styles from "./NumberBadge.module.scss";

interface NumberBadgeProps {
value: number;
color?: "green" | "red" | "blue" | "default";
color?: "green" | "red" | "blue" | "default" | "grey";
outline?: boolean;
className?: string;
"aria-label"?: string;
}

export const NumberBadge: React.FC<NumberBadgeProps> = ({ value, color, className, "aria-label": ariaLabel }) => {
export const NumberBadge: React.FC<NumberBadgeProps> = ({
value,
color,
className,
outline = false,
"aria-label": ariaLabel,
}) => {
const numberBadgeClassnames = classnames(styles.circle, className, {
[styles.default]: !color || color === "default",
[styles["grey--outline"]]: outline === true && color === "grey",
[styles["blue--outline"]]: outline === true && color === "blue",
[styles.green]: color === "green",
[styles.red]: color === "red",
[styles.blue]: color === "blue",
Expand Down
4 changes: 4 additions & 0 deletions airbyte-webapp/src/components/ui/Text/Text.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@
color: colors.$dark-blue;
}

.blue {
color: colors.$blue;
}

.grey {
color: colors.$grey-400;
}
Expand Down
3 changes: 2 additions & 1 deletion airbyte-webapp/src/components/ui/Text/Text.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import React, { HTMLAttributes } from "react";
import styles from "./Text.module.scss";

type TextSize = "xs" | "sm" | "md" | "lg" | "xl";
type TextColor = "darkBlue" | "grey" | "grey300" | "green" | "red";
type TextColor = "darkBlue" | "grey" | "grey300" | "green" | "red" | "blue";
type TextElementType = "p" | "span" | "div";
type TextHTMLElement = HTMLParagraphElement | HTMLSpanElement | HTMLDivElement;

Expand Down Expand Up @@ -36,6 +36,7 @@ const colors: Record<TextColor, string> = {
grey: styles.grey,
grey300: styles.grey300,
red: styles.red,
blue: styles.blue,
};

const textAlignments: Record<TextAlignment, string> = {
Expand Down
1 change: 1 addition & 0 deletions airbyte-webapp/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@
"connectionForm.namespaceFormat.placeholder": "My namespace",
"connectionForm.defineSource": "Define source",
"connectionForm.defineDestination": "Define destination",
"connectionForm.configureConnection": "Configure connection",
"connectionForm.destinationNew": "Set up a new destination",
"connectionForm.destinationNewDescription": "Configure a new destination from Airbyte's catalog of available connectors",
"connectionForm.sourceFormat": "Mirror source structure",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { NextPageHeaderWithNavigation } from "components/ui/PageHeader/NextPageH

import { ConnectionRoutePaths, RoutePaths } from "pages/routePaths";

import { CreateConnectionTitleBlock } from "../CreateConnectionPage/CreateConnectionTitleBlock";

export const ConfigureConnectionPage = () => {
const { formatMessage } = useIntl();
const { workspaceId } = useParams<{ workspaceId: string }>();
Expand Down Expand Up @@ -38,7 +40,11 @@ export const ConfigureConnectionPage = () => {
return (
<MainPageWithScroll
headTitle={<HeadTitle titles={[{ id: "connection.newConnectionTitle" }]} />}
pageTitle={<NextPageHeaderWithNavigation breadcrumbsData={breadcrumbsData} />}
pageTitle={
<NextPageHeaderWithNavigation breadcrumbsData={breadcrumbsData}>
<CreateConnectionTitleBlock />
</NextPageHeaderWithNavigation>
}
>
<CreateConnectionForm />
</MainPageWithScroll>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import { useMemo } from "react";
import { useIntl } from "react-intl";
import { Navigate, useSearchParams } from "react-router-dom";

import { MainPageWithScroll } from "components";
import { HeadTitle } from "components/common/HeadTitle";
import { MainPageWithScroll } from "components/common/MainPageWithScroll";
import { SelectDestination } from "components/connection/CreateConnection/SelectDestination";
import { SelectSource } from "components/connection/CreateConnection/SelectSource";
import { FormPageContent } from "components/ConnectorBlocks";
import { NextPageHeaderWithNavigation } from "components/ui/PageHeader/NextPageHeaderWithNavigation";

import { PageTrackingCodes, useTrackPage } from "core/services/analytics";
import { AppActionCodes } from "hooks/services/AppMonitoringService";
import { useGetDestination } from "hooks/services/useDestinationHook";
import { useGetSource } from "hooks/services/useSourceHook";
Expand All @@ -17,7 +18,10 @@ import { useCurrentWorkspaceId } from "services/workspaces/WorkspacesService";
import { trackAction } from "utils/datadog";
import { ConnectorDocumentationWrapper } from "views/Connector/ConnectorDocumentationLayout";

export const CreateConnectionPage = () => {
import { CreateConnectionTitleBlock } from "./CreateConnectionTitleBlock";

export const CreateConnectionPage: React.FC = () => {
useTrackPage(PageTrackingCodes.CONNECTIONS_NEW);
const { formatMessage } = useIntl();
const workspaceId = useCurrentWorkspaceId();
const [searchParams] = useSearchParams();
Expand Down Expand Up @@ -70,7 +74,11 @@ export const CreateConnectionPage = () => {
<ConnectorDocumentationWrapper>
<MainPageWithScroll
headTitle={<HeadTitle titles={[{ id: "connection.newConnectionTitle" }]} />}
pageTitle={<NextPageHeaderWithNavigation breadcrumbsData={breadcrumbsData} />}
pageTitle={
<NextPageHeaderWithNavigation breadcrumbsData={breadcrumbsData}>
<CreateConnectionTitleBlock />
</NextPageHeaderWithNavigation>
}
>
<FormPageContent>{currentStep}</FormPageContent>
</MainPageWithScroll>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
@use "scss/colors";
@use "scss/variables";
@use "scss/mixins";

.iconPlaceholder {
@include mixins.shadow-inset;

background-color: colors.$grey-50;
width: 32px;
height: 32px;
border-radius: variables.$border-radius-md;
}

.namePlaceholder {
@include mixins.shadow-inset;

width: variables.$width-connector-name-placeholder;
height: variables.$spacing-xl;
border-radius: variables.$border-radius-md;
background-color: colors.$grey-50;
}
Loading

0 comments on commit 7230d28

Please sign in to comment.