diff --git a/airbyte-webapp/.gitignore b/airbyte-webapp/.gitignore index 8e572d98ba496..4c2aede537753 100644 --- a/airbyte-webapp/.gitignore +++ b/airbyte-webapp/.gitignore @@ -32,3 +32,4 @@ storybook-static/ # Ignore generated API clients, since they're automatically generated /src/core/request/AirbyteClient.ts /src/core/request/ConnectorBuilderClient.ts +/src/core/request/ConnectorManifest.ts diff --git a/airbyte-webapp/orval.config.ts b/airbyte-webapp/orval.config.ts index f859eaa5e4e21..f32b8e5fa7691 100644 --- a/airbyte-webapp/orval.config.ts +++ b/airbyte-webapp/orval.config.ts @@ -43,4 +43,21 @@ export default defineConfig({ }, }, }, + connectorManifest: { + input: "./src/services/connectorBuilder/connector_manifest_openapi.yaml", + output: { + target: "./src/core/request/ConnectorManifest.ts", + prettier: true, + override: { + header: (info) => [ + `eslint-disable`, + `Generated by orval 🍺`, + `Do not edit manually. Run "npm run generate-client" instead.`, + ...(info.title ? [info.title] : []), + ...(info.description ? [info.description] : []), + ...(info.version ? [`OpenAPI spec version: ${info.version}`] : []), + ], + }, + }, + }, }); diff --git a/airbyte-webapp/public/images/octavia/pointing.svg b/airbyte-webapp/public/images/octavia/pointing.svg new file mode 100644 index 0000000000000..b4453c1fb31dd --- /dev/null +++ b/airbyte-webapp/public/images/octavia/pointing.svg @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/AddStreamButton.module.scss b/airbyte-webapp/src/components/connectorBuilder/Builder/AddStreamButton.module.scss new file mode 100644 index 0000000000000..6c1f9c41b9900 --- /dev/null +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/AddStreamButton.module.scss @@ -0,0 +1,20 @@ +@use "scss/variables"; +@use "scss/colors"; + +$buttonWidth: 26px; + +.body { + display: flex; + flex-direction: column; + gap: variables.$spacing-xl; +} + +.addButton { + width: $buttonWidth; + height: $buttonWidth !important; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + padding: 9px !important; +} diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/AddStreamButton.tsx b/airbyte-webapp/src/components/connectorBuilder/Builder/AddStreamButton.tsx new file mode 100644 index 0000000000000..2177db82409c1 --- /dev/null +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/AddStreamButton.tsx @@ -0,0 +1,101 @@ +import { Form, Formik, useField } from "formik"; +import { useState } from "react"; +import { FormattedMessage, useIntl } from "react-intl"; + +import { Button } from "components/ui/Button"; +import { Modal, ModalBody, ModalFooter } from "components/ui/Modal"; + +import { FormikPatch } from "core/form/FormikPatch"; + +import { ReactComponent as PlusIcon } from "../../connection/ConnectionOnboarding/plusIcon.svg"; +import { BuilderStream } from "../types"; +import styles from "./AddStreamButton.module.scss"; +import { BuilderField } from "./BuilderField"; + +interface AddStreamValues { + streamName: string; + urlPath: string; +} + +interface AddStreamButtonProps { + onAddStream: (addedStreamNum: number) => void; +} + +export const AddStreamButton: React.FC = ({ onAddStream }) => { + const { formatMessage } = useIntl(); + const [isOpen, setIsOpen] = useState(false); + const [streamsField, , helpers] = useField("streams"); + const numStreams = streamsField.value.length; + + return ( + <> + + + + + + + + )} + + ); +}; diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/Builder.module.scss b/airbyte-webapp/src/components/connectorBuilder/Builder/Builder.module.scss new file mode 100644 index 0000000000000..108a100d0dfb4 --- /dev/null +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/Builder.module.scss @@ -0,0 +1,24 @@ +@use "scss/variables"; +@use "scss/colors"; +@use "scss/mixins"; + +.container { + height: 100%; + display: flex; +} + +.sidebar { + @include mixins.right-shadow; + + width: 200px; + flex: 0 0 auto; + background-color: colors.$white; +} + +.form { + flex: 1; + padding: variables.$spacing-xl; + display: flex; + flex-direction: column; + gap: variables.$spacing-xl; +} diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/Builder.tsx b/airbyte-webapp/src/components/connectorBuilder/Builder/Builder.tsx new file mode 100644 index 0000000000000..fb40a24a2d9f4 --- /dev/null +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/Builder.tsx @@ -0,0 +1,31 @@ +import { Form } from "formik"; +import { useEffect } from "react"; + +import { useConnectorBuilderState } from "services/connectorBuilder/ConnectorBuilderStateService"; + +import { BuilderFormValues } from "../types"; +import styles from "./Builder.module.scss"; +import { BuilderSidebar } from "./BuilderSidebar"; +import { GlobalConfigView } from "./GlobalConfigView"; +import { StreamConfigView } from "./StreamConfigView"; + +interface BuilderProps { + values: BuilderFormValues; + toggleYamlEditor: () => void; +} + +export const Builder: React.FC = ({ values, toggleYamlEditor }) => { + const { setBuilderFormValues, selectedView } = useConnectorBuilderState(); + useEffect(() => { + setBuilderFormValues(values); + }, [values, setBuilderFormValues]); + + return ( +
+ +
+ {selectedView === "global" ? : } + +
+ ); +}; diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderCard.module.scss b/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderCard.module.scss new file mode 100644 index 0000000000000..48fedff9a2a0c --- /dev/null +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderCard.module.scss @@ -0,0 +1,8 @@ +@use "scss/variables"; + +.card { + padding: variables.$spacing-xl; + display: flex; + flex-direction: column; + gap: variables.$spacing-xl; +} diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderCard.tsx b/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderCard.tsx new file mode 100644 index 0000000000000..9bbb968a3d29c --- /dev/null +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderCard.tsx @@ -0,0 +1,9 @@ +import React from "react"; + +import { Card } from "components/ui/Card"; + +import styles from "./BuilderCard.module.scss"; + +export const BuilderCard: React.FC> = ({ children }) => { + return {children}; +}; diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderField.module.scss b/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderField.module.scss new file mode 100644 index 0000000000000..f198f89561f5b --- /dev/null +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderField.module.scss @@ -0,0 +1,7 @@ +@use "scss/variables"; +@use "scss/colors"; + +.error { + margin-top: variables.$spacing-sm; + color: colors.$red; +} diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderField.tsx b/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderField.tsx new file mode 100644 index 0000000000000..bd799ff8efed2 --- /dev/null +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderField.tsx @@ -0,0 +1,98 @@ +import { useField } from "formik"; +import { FormattedMessage } from "react-intl"; +import * as yup from "yup"; + +import { ControlLabels } from "components/LabeledControl"; +import { DropDown } from "components/ui/DropDown"; +import { Input } from "components/ui/Input"; +import { TagInput } from "components/ui/TagInput"; +import { Text } from "components/ui/Text"; + +import styles from "./BuilderField.module.scss"; + +interface EnumFieldProps { + options: string[]; + value: string; + setValue: (value: string) => void; + error: boolean; +} + +interface ArrayFieldProps { + name: string; + value: string[]; + setValue: (value: string[]) => void; + error: boolean; +} + +interface BaseFieldProps { + // path to the location in the Connector Manifest schema which should be set by this component + path: string; + label: string; + tooltip?: string; + optional?: boolean; +} + +type BuilderFieldProps = BaseFieldProps & ({ type: "text" } | { type: "array" } | { type: "enum"; options: string[] }); + +const EnumField: React.FC = ({ options, value, setValue, error, ...props }) => { + return ( + { + return { label: option, value: option }; + })} + onChange={(selected) => selected && setValue(selected.value)} + value={value} + error={error} + /> + ); +}; + +const ArrayField: React.FC = ({ name, value, setValue, error }) => { + return setValue(value)} error={error} />; +}; + +export const BuilderField: React.FC = ({ path, label, tooltip, optional = false, ...props }) => { + let yupSchema = props.type === "array" ? yup.array().of(yup.string()) : yup.string(); + if (!optional) { + yupSchema = yupSchema.required("form.empty.error"); + } + const fieldConfig = { + name: path, + validate: (value: string) => { + try { + yupSchema.validateSync(value); + return undefined; + } catch (err) { + if (err instanceof yup.ValidationError) { + return err.errors.join(", "); + } + throw err; + } + }, + }; + const [field, meta, helpers] = useField(fieldConfig); + const hasError = !!meta.error && meta.touched; + + return ( + + {props.type === "text" && } + {props.type === "array" && ( + + )} + {props.type === "enum" && ( + + )} + {hasError && ( + + + + )} + + ); +}; diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderSidebar.module.scss b/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderSidebar.module.scss new file mode 100644 index 0000000000000..6bedeb454d3a3 --- /dev/null +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderSidebar.module.scss @@ -0,0 +1,104 @@ +@use "scss/variables"; +@use "scss/colors"; +@use "scss/mixins"; + +.container { + padding: variables.$spacing-sm; + display: flex; + flex-direction: column; + align-items: center; + position: relative; +} + +.connectorImg { + @include mixins.shadow; + + width: 90px; + border-radius: 25px; + padding: variables.$spacing-xl; + margin-top: 55px; +} + +.connectorName { + margin-top: variables.$spacing-md; + min-height: 28px; + width: 100%; + display: flex; + align-items: center; + overflow-x: auto; +} + +.connectorNameText { + margin-left: auto; + margin-right: auto; +} + +.streamsHeader { + display: flex; + align-items: center; + width: 100%; + padding: 0 variables.$spacing-sm 0 variables.$spacing-md; + margin-top: variables.$spacing-2xl; +} + +.streamsHeading { + color: colors.$blue; + flex-grow: 1; +} + +.downloadButton { + width: 100%; +} + +.resetButton { + margin-top: variables.$spacing-sm; +} + +.viewButton { + border: none; + width: 100%; + height: 34px; + flex-shrink: 0; + cursor: pointer; + border-radius: variables.$border-radius-md; + padding: variables.$spacing-md; + display: flex; + align-items: center; + gap: variables.$spacing-sm; + overflow: hidden; +} + +.unselectedViewButton { + background-color: transparent; + color: colors.$dark-blue; + + &:hover { + background-color: colors.$grey-50; + } +} + +.selectedViewButton { + background-color: colors.$dark-blue; + color: colors.$white; +} + +.globalConfigButton { + margin-top: variables.$spacing-xl; +} + +.streamList { + flex-grow: 1; + width: 100%; + margin: variables.$spacing-md 0; + display: flex; + flex-direction: column; + overflow-y: auto; +} + +.streamViewText { + color: inherit; +} + +.emptyStreamViewText { + color: colors.$grey-300; +} diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderSidebar.tsx b/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderSidebar.tsx new file mode 100644 index 0000000000000..2a2b40827cb07 --- /dev/null +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderSidebar.tsx @@ -0,0 +1,132 @@ +import { faSliders } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import classnames from "classnames"; +import { useFormikContext } from "formik"; +import { FormattedMessage, useIntl } from "react-intl"; + +import { Button } from "components/ui/Button"; +import { Heading } from "components/ui/Heading"; +import { Text } from "components/ui/Text"; + +import { useConfirmationModalService } from "hooks/services/ConfirmationModal"; +import { + BuilderView, + DEFAULT_BUILDER_FORM_VALUES, + useConnectorBuilderState, +} from "services/connectorBuilder/ConnectorBuilderStateService"; + +import { DownloadYamlButton } from "../DownloadYamlButton"; +import { BuilderFormValues } from "../types"; +import { AddStreamButton } from "./AddStreamButton"; +import styles from "./BuilderSidebar.module.scss"; +import { UiYamlToggleButton } from "./UiYamlToggleButton"; + +interface ViewSelectButtonProps { + className?: string; + selected: boolean; + onClick: () => void; +} + +const ViewSelectButton: React.FC> = ({ + children, + className, + selected, + onClick, +}) => { + return ( + + ); +}; + +interface BuilderSidebarProps { + className?: string; + toggleYamlEditor: () => void; +} + +export const BuilderSidebar: React.FC = ({ className, toggleYamlEditor }) => { + const { formatMessage } = useIntl(); + const { openConfirmationModal, closeConfirmationModal } = useConfirmationModalService(); + const { yamlManifest, selectedView, setSelectedView, setTestStreamIndex } = useConnectorBuilderState(); + const { values, setValues } = useFormikContext(); + const handleResetForm = () => { + openConfirmationModal({ + text: "connectorBuilder.resetModal.text", + title: "connectorBuilder.resetModal.title", + submitButtonText: "connectorBuilder.resetModal.submitButton", + onSubmit: () => { + setValues(DEFAULT_BUILDER_FORM_VALUES); + setSelectedView("global"); + closeConfirmationModal(); + }, + }); + }; + const handleViewSelect = (selectedView: BuilderView) => { + setSelectedView(selectedView); + if (selectedView !== "global") { + setTestStreamIndex(selectedView); + } + }; + + return ( +
+ + + {/* TODO: replace with uploaded img when that functionality is added */} + {formatMessage({ + +
+ + {values.global?.connectorName} + +
+ + handleViewSelect("global")} + > + + + + +
+ + + + + handleViewSelect(addedStreamNum)} /> +
+ +
+ {values.streams.map(({ name }, num) => ( + handleViewSelect(num)}> + {name && name.trim() ? ( + {name} + ) : ( + + + + )} + + ))} +
+ + + +
+ ); +}; diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/GlobalConfigView.tsx b/airbyte-webapp/src/components/connectorBuilder/Builder/GlobalConfigView.tsx new file mode 100644 index 0000000000000..1d1a40a945180 --- /dev/null +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/GlobalConfigView.tsx @@ -0,0 +1,21 @@ +import { BuilderCard } from "./BuilderCard"; +import { BuilderField } from "./BuilderField"; + +export const GlobalConfigView: React.FC = () => { + return ( + <> + {/* Not using intl for the labels and tooltips in this component in order to keep maintainence simple */} + + + + + + + + ); +}; diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/StreamConfigView.tsx b/airbyte-webapp/src/components/connectorBuilder/Builder/StreamConfigView.tsx new file mode 100644 index 0000000000000..a59b23b3b533c --- /dev/null +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/StreamConfigView.tsx @@ -0,0 +1,36 @@ +import { BuilderCard } from "./BuilderCard"; +import { BuilderField } from "./BuilderField"; + +interface StreamConfigViewProps { + streamNum: number; +} + +export const StreamConfigView: React.FC = ({ streamNum }) => { + const streamPath = (path: string) => `streams[${streamNum}].${path}`; + + return ( + + {/* Not using intl for the labels and tooltips in this component in order to keep maintainence simple */} + + + + + + ); +}; diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/UiYamlToggleButton.module.scss b/airbyte-webapp/src/components/connectorBuilder/Builder/UiYamlToggleButton.module.scss new file mode 100644 index 0000000000000..53e26ff34d47d --- /dev/null +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/UiYamlToggleButton.module.scss @@ -0,0 +1,38 @@ +@use "scss/variables"; +@use "scss/colors"; + +.button { + cursor: pointer; + border: variables.$border-thin solid colors.$dark-blue; + background-color: colors.$dark-blue; + border-radius: variables.$border-radius-sm; + padding: 0; + overflow: hidden; + + // absolute positioning so it is in the same spot in both the ui and yaml views + position: absolute; + top: 15px; + left: 51px; + display: grid; + grid-template: 1fr / 1fr 1fr; +} + +.text { + height: 100%; + flex: 1; + display: flex; + align-items: center; + justify-content: center; + font-weight: 700; + padding: 4px 8px; +} + +.selected { + background-color: transparent; + color: colors.$white; +} + +.unselected { + background-color: colors.$white; + color: colors.$dark-blue; +} diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/UiYamlToggleButton.tsx b/airbyte-webapp/src/components/connectorBuilder/Builder/UiYamlToggleButton.tsx new file mode 100644 index 0000000000000..e92f1cb251261 --- /dev/null +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/UiYamlToggleButton.tsx @@ -0,0 +1,39 @@ +import classnames from "classnames"; +import { FormattedMessage } from "react-intl"; + +import { Text } from "components/ui/Text"; + +import styles from "./UiYamlToggleButton.module.scss"; + +interface UiYamlToggleButtonProps { + className?: string; + yamlSelected: boolean; + onClick: () => void; +} + +export const UiYamlToggleButton: React.FC = ({ className, yamlSelected, onClick }) => { + return ( + + ); +}; diff --git a/airbyte-webapp/src/components/connectorBuilder/YamlEditor/DownloadYamlButton.tsx b/airbyte-webapp/src/components/connectorBuilder/DownloadYamlButton.tsx similarity index 79% rename from airbyte-webapp/src/components/connectorBuilder/YamlEditor/DownloadYamlButton.tsx rename to airbyte-webapp/src/components/connectorBuilder/DownloadYamlButton.tsx index f5a49a5fc99ea..a807239e778fa 100644 --- a/airbyte-webapp/src/components/connectorBuilder/YamlEditor/DownloadYamlButton.tsx +++ b/airbyte-webapp/src/components/connectorBuilder/DownloadYamlButton.tsx @@ -22,7 +22,7 @@ export const DownloadYamlButton: React.FC = ({ classNam const downloadButton = ( ); - return yamlIsValid ? ( - downloadButton - ) : ( - - - + return ( +
+ {yamlIsValid ? ( + downloadButton + ) : ( + + + + )} +
); }; diff --git a/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/PageDisplay.module.scss b/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/PageDisplay.module.scss index 9f05ee6dc40dd..5964f623492d1 100644 --- a/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/PageDisplay.module.scss +++ b/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/PageDisplay.module.scss @@ -35,16 +35,3 @@ max-height: 100%; overflow-y: auto; } - -// add a fade at the bottom of the tabPanel -.tabPanel::after { - content: ""; - position: absolute; - z-index: 1; - bottom: 0; - left: 0; - pointer-events: none; - background-image: linear-gradient(to bottom, colors.$transparentColor, colors.$white 100%); - width: 100%; - height: variables.$spacing-xl; -} diff --git a/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamSelector.module.scss b/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamSelector.module.scss index 7f297b2eb5cf7..14242399efdc4 100644 --- a/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamSelector.module.scss +++ b/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamSelector.module.scss @@ -1,9 +1,7 @@ @use "scss/variables"; @use "scss/colors"; -.centered { - margin-left: auto; - margin-right: auto; +.container { width: 75%; max-width: 320px; } @@ -11,14 +9,17 @@ .button { padding: variables.$spacing-md; background-color: transparent; - border-radius: variables.$border-radius-lg; + border-radius: variables.$border-radius-md; border: none; display: flex; justify-content: center; + gap: variables.$spacing-md; +} + +.label { + overflow: auto; } .arrow { - margin-left: variables.$spacing-md; - margin-top: -1px; color: colors.$blue; } diff --git a/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamSelector.tsx b/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamSelector.tsx index 300dd13c1f01f..bfad00ad7418a 100644 --- a/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamSelector.tsx +++ b/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamSelector.tsx @@ -2,25 +2,23 @@ import { faSortDown } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import classNames from "classnames"; import capitalize from "lodash/capitalize"; +import { useIntl } from "react-intl"; import { Heading } from "components/ui/Heading"; import { ListBox, ListBoxControlButtonProps } from "components/ui/ListBox"; -import { StreamsListReadStreamsItem } from "core/request/ConnectorBuilderClient"; import { useConnectorBuilderState } from "services/connectorBuilder/ConnectorBuilderStateService"; import styles from "./StreamSelector.module.scss"; interface StreamSelectorProps { className?: string; - streams: StreamsListReadStreamsItem[]; - selectedStream: StreamsListReadStreamsItem; } const ControlButton: React.FC> = ({ selectedOption }) => { return ( <> - + {selectedOption.label} @@ -28,18 +26,32 @@ const ControlButton: React.FC> = ({ selectedOp ); }; -export const StreamSelector: React.FC = ({ className, streams, selectedStream }) => { - const { setSelectedStream } = useConnectorBuilderState(); +export const StreamSelector: React.FC = ({ className }) => { + const { formatMessage } = useIntl(); + const { streams, selectedView, testStreamIndex, setSelectedView, setTestStreamIndex } = useConnectorBuilderState(); const options = streams.map((stream) => { - return { label: capitalize(stream.name), value: stream.name }; + const label = + stream.name && stream.name.trim() ? capitalize(stream.name) : formatMessage({ id: "connectorBuilder.emptyName" }); + return { label, value: stream.name }; }); + const handleStreamSelect = (selectedStreamName: string) => { + const selectedStreamIndex = streams.findIndex((stream) => selectedStreamName === stream.name); + if (selectedStreamIndex >= 0) { + setTestStreamIndex(selectedStreamIndex); + + if (selectedView !== "global" && selectedStreamIndex >= 0) { + setSelectedView(selectedStreamIndex); + } + } + }; + return ( diff --git a/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamTester.module.scss b/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamTester.module.scss index 0d538a3b55b88..c4ad3c80734c0 100644 --- a/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamTester.module.scss +++ b/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamTester.module.scss @@ -8,6 +8,7 @@ align-items: center; gap: variables.$spacing-lg; min-height: 0; + width: 100%; } .resizablePanelsContainer { @@ -18,10 +19,6 @@ z-index: 0; } -.testButton { - width: 100%; -} - .testButtonTooltipContainer { width: 100%; } diff --git a/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamTester.tsx b/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamTester.tsx index 3cb1cfa7e4a1e..2bb0459b3a965 100644 --- a/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamTester.tsx +++ b/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamTester.tsx @@ -10,7 +10,6 @@ import { Spinner } from "components/ui/Spinner"; import { Text } from "components/ui/Text"; import { Tooltip } from "components/ui/Tooltip"; -import { StreamsListReadStreamsItem } from "core/request/ConnectorBuilderClient"; import { useReadStream } from "services/connectorBuilder/ConnectorBuilderApiService"; import { useConnectorBuilderState } from "services/connectorBuilder/ConnectorBuilderStateService"; @@ -18,13 +17,9 @@ import { LogsDisplay } from "./LogsDisplay"; import { ResultDisplay } from "./ResultDisplay"; import styles from "./StreamTester.module.scss"; -interface StreamTesterProps { - selectedStream: StreamsListReadStreamsItem; -} - -export const StreamTester: React.FC = ({ selectedStream }) => { +export const StreamTester: React.FC = () => { const { formatMessage } = useIntl(); - const { jsonManifest, configJson, yamlIsValid } = useConnectorBuilderState(); + const { jsonManifest, configJson, yamlIsValid, streams, testStreamIndex } = useConnectorBuilderState(); const { data: streamReadData, refetch: readStream, @@ -33,7 +28,7 @@ export const StreamTester: React.FC = ({ selectedStream }) => isFetching, } = useReadStream({ manifest: jsonManifest, - stream: selectedStream.name, + stream: streams[testStreamIndex]?.name, config: configJson, }); @@ -60,7 +55,7 @@ export const StreamTester: React.FC = ({ selectedStream }) => const testButton = (