From 3e10c095a7d4efc725ffaf5e6e619cdb88904132 Mon Sep 17 00:00:00 2001 From: Lake Mossman Date: Tue, 13 Dec 2022 11:17:03 -0800 Subject: [PATCH] [Connector Builder] Config UI error handling (#20280) * move connector builder components into the same shared components/connectorBuilder directory * move diff over from poc branch * save current progress * add modal for adding streams * focus stream after adding and reset button style * add reset confirm modal and select view on add * style global config and streams buttons * styling improvements * handle long stream names better * pull in connector manifest schema directly * add box shadows to resizable panels * upgrade orval and use connector manifest schema directly * remove airbyte protocol from connector builder api spec * generate python models from openapi change * fix position of yaml toggle * handle no stream case with better looking message * group global fields into single object and fix console error * confirmation modal on toggling dirty form + cleanup * fix connector name display * undo change to manifest schema * remove commented code * remove unnecessary change * fix spacing * use shadow mixin for connector img * add comment about connector img * save progress * change onSubmit to no-op * remove console log * clean up styling * simplify sidebar to remove StreamSelectButton component * swap colors of toggle * move FormikPatch to src/core/form * move types up to connectorBuilder/ level * use grid display for ui yaml toggle button * use spread instead of setting array index directly * add intl in missing places * pull connector manifest schema in through separate openapi spec * disable test and download buttons when there are errors and touch erroring fields * fix comment * add error indicators to view buttons * remove commented code * use correct intl string id * throttle setting json manifest in yaml editor * use button prop instead of manually styling * consolidate AddStreamButton styles * fix sidebar flex styles * use specific flex properties instead of flex * clean up download and reset button styles * use row-reverse for yaml editor download button * fix stream selector styles to remove margins * give connector setup guide panel same corner and shadow styles * remove blur from page display * set view to stream when selected in test panel * add placeholder when stream name is empty * switch to index-based stream selection to preserve testing panel selected stream on rename * handle empty name in stream selector * remove unnecessary margin * only show error indicator for touched fields * focus erroring view on test click * show warning icon and tooltip when there are form errors * remove unused parameter --- .../connectorBuilder/Builder/BuilderField.tsx | 21 +---- .../Builder/BuilderSidebar.module.scss | 13 ++++ .../Builder/BuilderSidebar.tsx | 16 +++- .../DownloadYamlButton.module.scss | 3 + .../connectorBuilder/DownloadYamlButton.tsx | 51 +++++++++++-- .../StreamTestButton.module.scss | 14 ++++ .../StreamTestingPanel/StreamTestButton.tsx | 76 +++++++++++++++++++ .../StreamTester.module.scss | 8 -- .../StreamTestingPanel/StreamTester.tsx | 44 ++--------- .../YamlEditor/YamlEditor.tsx | 2 + .../src/components/connectorBuilder/types.ts | 17 +++++ .../connectorBuilder/useBuilderErrors.ts | 73 ++++++++++++++++++ airbyte-webapp/src/locales/en.json | 6 +- .../ConnectorBuilderPage.tsx | 16 ++-- .../ConnectorBuilderStateService.tsx | 7 ++ 15 files changed, 280 insertions(+), 87 deletions(-) create mode 100644 airbyte-webapp/src/components/connectorBuilder/DownloadYamlButton.module.scss create mode 100644 airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamTestButton.module.scss create mode 100644 airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamTestButton.tsx create mode 100644 airbyte-webapp/src/components/connectorBuilder/useBuilderErrors.ts diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderField.tsx b/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderField.tsx index bd799ff8efed2c..004c6e36c6a07a 100644 --- a/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderField.tsx +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderField.tsx @@ -1,6 +1,5 @@ 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"; @@ -53,25 +52,7 @@ const ArrayField: React.FC = ({ name, value, setValue, 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 [field, meta, helpers] = useField(path); const hasError = !!meta.error && meta.touched; return ( diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderSidebar.module.scss b/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderSidebar.module.scss index 6bedeb454d3a39..ee4927d9320c6f 100644 --- a/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderSidebar.module.scss +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderSidebar.module.scss @@ -95,6 +95,19 @@ overflow-y: auto; } +.viewLabel { + flex: 1; + overflow: hidden; + display: flex; + gap: variables.$spacing-sm; + align-items: center; +} + +.errorIndicator { + flex: 0 0 auto; + margin-right: variables.$spacing-xs; +} + .streamViewText { color: inherit; } diff --git a/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderSidebar.tsx b/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderSidebar.tsx index 2a2b40827cb074..11a2f53b553e35 100644 --- a/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderSidebar.tsx +++ b/airbyte-webapp/src/components/connectorBuilder/Builder/BuilderSidebar.tsx @@ -4,6 +4,7 @@ import classnames from "classnames"; import { useFormikContext } from "formik"; import { FormattedMessage, useIntl } from "react-intl"; +import Indicator from "components/Indicator"; import { Button } from "components/ui/Button"; import { Heading } from "components/ui/Heading"; import { Text } from "components/ui/Text"; @@ -17,6 +18,7 @@ import { import { DownloadYamlButton } from "../DownloadYamlButton"; import { BuilderFormValues } from "../types"; +import { useBuilderErrors } from "../useBuilderErrors"; import { AddStreamButton } from "./AddStreamButton"; import styles from "./BuilderSidebar.module.scss"; import { UiYamlToggleButton } from "./UiYamlToggleButton"; @@ -24,6 +26,7 @@ import { UiYamlToggleButton } from "./UiYamlToggleButton"; interface ViewSelectButtonProps { className?: string; selected: boolean; + showErrorIndicator: boolean; onClick: () => void; } @@ -31,6 +34,7 @@ const ViewSelectButton: React.FC> children, className, selected, + showErrorIndicator, onClick, }) => { return ( @@ -41,7 +45,8 @@ const ViewSelectButton: React.FC> })} onClick={onClick} > - {children} +
{children}
+ {showErrorIndicator && } ); }; @@ -53,6 +58,7 @@ interface BuilderSidebarProps { export const BuilderSidebar: React.FC = ({ className, toggleYamlEditor }) => { const { formatMessage } = useIntl(); + const { hasErrors } = useBuilderErrors(); const { openConfirmationModal, closeConfirmationModal } = useConfirmationModalService(); const { yamlManifest, selectedView, setSelectedView, setTestStreamIndex } = useConnectorBuilderState(); const { values, setValues } = useFormikContext(); @@ -95,6 +101,7 @@ export const BuilderSidebar: React.FC = ({ className, toggl handleViewSelect("global")} > @@ -111,7 +118,12 @@ export const BuilderSidebar: React.FC = ({ className, toggl
{values.streams.map(({ name }, num) => ( - handleViewSelect(num)}> + handleViewSelect(num)} + > {name && name.trim() ? ( {name} ) : ( diff --git a/airbyte-webapp/src/components/connectorBuilder/DownloadYamlButton.module.scss b/airbyte-webapp/src/components/connectorBuilder/DownloadYamlButton.module.scss new file mode 100644 index 00000000000000..693e901fd3e1f3 --- /dev/null +++ b/airbyte-webapp/src/components/connectorBuilder/DownloadYamlButton.module.scss @@ -0,0 +1,3 @@ +.tooltipContainer { + display: block; +} diff --git a/airbyte-webapp/src/components/connectorBuilder/DownloadYamlButton.tsx b/airbyte-webapp/src/components/connectorBuilder/DownloadYamlButton.tsx index a807239e778fa8..55a6d1f0170a3a 100644 --- a/airbyte-webapp/src/components/connectorBuilder/DownloadYamlButton.tsx +++ b/airbyte-webapp/src/components/connectorBuilder/DownloadYamlButton.tsx @@ -5,8 +5,12 @@ import { FormattedMessage } from "react-intl"; import { Button } from "components/ui/Button"; import { Tooltip } from "components/ui/Tooltip"; +import { useConnectorBuilderState } from "services/connectorBuilder/ConnectorBuilderStateService"; import { downloadFile } from "utils/file"; +import styles from "./DownloadYamlButton.module.scss"; +import { useBuilderErrors } from "./useBuilderErrors"; + interface DownloadYamlButtonProps { className?: string; yaml: string; @@ -14,18 +18,45 @@ interface DownloadYamlButtonProps { } export const DownloadYamlButton: React.FC = ({ className, yaml, yamlIsValid }) => { + const { editorView } = useConnectorBuilderState(); + const { hasErrors, validateAndTouch } = useBuilderErrors(); + const downloadYaml = () => { const file = new Blob([yaml], { type: "text/plain;charset=utf-8" }); // TODO: pull name from connector name input or generate from yaml contents downloadFile(file, "connector_builder.yaml"); }; + const handleClick = () => { + if (editorView === "yaml") { + downloadYaml(); + return; + } + + validateAndTouch(downloadYaml); + }; + + let buttonDisabled = false; + let showWarningIcon = false; + let tooltipContent = undefined; + + if (editorView === "yaml" && !yamlIsValid) { + buttonDisabled = true; + showWarningIcon = true; + tooltipContent = ; + } + + if (editorView === "ui" && hasErrors(true)) { + showWarningIcon = true; + tooltipContent = ; + } + const downloadButton = ( @@ -33,12 +64,16 @@ export const DownloadYamlButton: React.FC = ({ classNam return (
- {yamlIsValid ? ( - downloadButton - ) : ( - - + {tooltipContent !== undefined ? ( + + {tooltipContent} + ) : ( + downloadButton )}
); diff --git a/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamTestButton.module.scss b/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamTestButton.module.scss new file mode 100644 index 00000000000000..fcdb4f118089fc --- /dev/null +++ b/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamTestButton.module.scss @@ -0,0 +1,14 @@ +@use "scss/variables"; +@use "scss/colors"; + +.testButton { + width: 100%; +} + +.testButtonTooltipContainer { + width: 100%; +} + +.testButtonText { + color: colors.$white; +} diff --git a/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamTestButton.tsx b/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamTestButton.tsx new file mode 100644 index 00000000000000..482893c176e029 --- /dev/null +++ b/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamTestButton.tsx @@ -0,0 +1,76 @@ +import { faWarning } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { FormattedMessage } from "react-intl"; + +import { RotateIcon } from "components/icons/RotateIcon"; +import { Button } from "components/ui/Button"; +import { Text } from "components/ui/Text"; +import { Tooltip } from "components/ui/Tooltip"; + +import { useConnectorBuilderState } from "services/connectorBuilder/ConnectorBuilderStateService"; + +import { useBuilderErrors } from "../useBuilderErrors"; +import styles from "./StreamTestButton.module.scss"; + +interface StreamTestButtonProps { + readStream: () => void; +} + +export const StreamTestButton: React.FC = ({ readStream }) => { + const { editorView, yamlIsValid, testStreamIndex } = useConnectorBuilderState(); + const { hasErrors, validateAndTouch } = useBuilderErrors(); + + const handleClick = () => { + if (editorView === "yaml") { + readStream(); + return; + } + + validateAndTouch(readStream, ["global", testStreamIndex]); + }; + + let buttonDisabled = false; + let showWarningIcon = false; + let tooltipContent = undefined; + + if (editorView === "yaml" && !yamlIsValid) { + buttonDisabled = true; + showWarningIcon = true; + tooltipContent = ; + } + + if (editorView === "ui" && hasErrors(true, ["global", testStreamIndex])) { + showWarningIcon = true; + tooltipContent = ; + } + + const testButton = ( + + ); + + return tooltipContent !== undefined ? ( + + {tooltipContent} + + ) : ( + testButton + ); +}; diff --git a/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamTester.module.scss b/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamTester.module.scss index c4ad3c80734c02..7cbf936ac5369e 100644 --- a/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamTester.module.scss +++ b/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamTester.module.scss @@ -19,14 +19,6 @@ z-index: 0; } -.testButtonTooltipContainer { - width: 100%; -} - -.testButtonText { - color: colors.$white; -} - .url { color: colors.$blue; font-weight: 400; diff --git a/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamTester.tsx b/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamTester.tsx index 2bb0459b3a965d..9c0908cd3ac148 100644 --- a/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamTester.tsx +++ b/airbyte-webapp/src/components/connectorBuilder/StreamTestingPanel/StreamTester.tsx @@ -1,25 +1,21 @@ -import { faWarning } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { useEffect, useState } from "react"; -import { FormattedMessage, useIntl } from "react-intl"; +import { useIntl } from "react-intl"; -import { RotateIcon } from "components/icons/RotateIcon"; -import { Button } from "components/ui/Button"; import { ResizablePanels } from "components/ui/ResizablePanels"; import { Spinner } from "components/ui/Spinner"; import { Text } from "components/ui/Text"; -import { Tooltip } from "components/ui/Tooltip"; import { useReadStream } from "services/connectorBuilder/ConnectorBuilderApiService"; import { useConnectorBuilderState } from "services/connectorBuilder/ConnectorBuilderStateService"; import { LogsDisplay } from "./LogsDisplay"; import { ResultDisplay } from "./ResultDisplay"; +import { StreamTestButton } from "./StreamTestButton"; import styles from "./StreamTester.module.scss"; export const StreamTester: React.FC = () => { const { formatMessage } = useIntl(); - const { jsonManifest, configJson, yamlIsValid, streams, testStreamIndex } = useConnectorBuilderState(); + const { jsonManifest, configJson, streams, testStreamIndex } = useConnectorBuilderState(); const { data: streamReadData, refetch: readStream, @@ -53,42 +49,14 @@ export const StreamTester: React.FC = () => { } }, [isError]); - const testButton = ( - - ); - return (
{streams[testStreamIndex]?.url} - {yamlIsValid ? ( - testButton - ) : ( - - - - )} + + + {isFetching && (
diff --git a/airbyte-webapp/src/components/connectorBuilder/YamlEditor/YamlEditor.tsx b/airbyte-webapp/src/components/connectorBuilder/YamlEditor/YamlEditor.tsx index b752b539d635c2..39353461c95f6b 100644 --- a/airbyte-webapp/src/components/connectorBuilder/YamlEditor/YamlEditor.tsx +++ b/airbyte-webapp/src/components/connectorBuilder/YamlEditor/YamlEditor.tsx @@ -87,11 +87,13 @@ export const YamlEditor: React.FC = ({ toggleYamlEditor }) => { title: "connectorBuilder.toggleModal.title", submitButtonText: "connectorBuilder.toggleModal.submitButton", onSubmit: () => { + setYamlIsValid(true); toggleYamlEditor(); closeConfirmationModal(); }, }); } else { + setYamlIsValid(true); toggleYamlEditor(); } }; diff --git a/airbyte-webapp/src/components/connectorBuilder/types.ts b/airbyte-webapp/src/components/connectorBuilder/types.ts index 5917ec348ea46e..e50faaf7f986e9 100644 --- a/airbyte-webapp/src/components/connectorBuilder/types.ts +++ b/airbyte-webapp/src/components/connectorBuilder/types.ts @@ -1,3 +1,5 @@ +import * as yup from "yup"; + import { ConnectorManifest, DeclarativeStream } from "core/request/ConnectorManifest"; export interface BuilderFormValues { @@ -15,6 +17,21 @@ export interface BuilderStream { httpMethod: "GET" | "POST"; } +export const builderFormValidationSchema = yup.object().shape({ + global: yup.object().shape({ + connectorName: yup.string().required("form.empty.error"), + urlBase: yup.string().required("form.empty.error"), + }), + streams: yup.array().of( + yup.object().shape({ + name: yup.string().required("form.empty.error"), + urlPath: yup.string().required("form.empty.error"), + fieldPointer: yup.array().of(yup.string()), + httpMethod: yup.mixed().oneOf(["GET", "POST"]), + }) + ), +}); + export const convertToManifest = (values: BuilderFormValues): ConnectorManifest => { const manifestStreams: DeclarativeStream[] = values.streams.map((stream) => { return { diff --git a/airbyte-webapp/src/components/connectorBuilder/useBuilderErrors.ts b/airbyte-webapp/src/components/connectorBuilder/useBuilderErrors.ts new file mode 100644 index 00000000000000..b2adfb073cca96 --- /dev/null +++ b/airbyte-webapp/src/components/connectorBuilder/useBuilderErrors.ts @@ -0,0 +1,73 @@ +import { flatten } from "flat"; +import { FormikErrors, useFormikContext } from "formik"; +import intersection from "lodash/intersection"; + +import { BuilderView, useConnectorBuilderState } from "services/connectorBuilder/ConnectorBuilderStateService"; + +import { BuilderFormValues } from "./types"; + +export const useBuilderErrors = () => { + const { touched, errors, validateForm, setFieldTouched } = useFormikContext(); + const { setSelectedView, setTestStreamIndex } = useConnectorBuilderState(); + + const invalidViews = ( + ignoreUntouched: boolean, + limitToViews?: BuilderView[], + inputErrors?: FormikErrors + ) => { + const errorsToCheck = inputErrors !== undefined ? inputErrors : errors; + + const errorKeys = ignoreUntouched + ? intersection(Object.keys(errorsToCheck), Object.keys(touched)) + : Object.keys(errorsToCheck); + + const invalidViews: BuilderView[] = []; + + if (errorKeys.includes("global")) { + invalidViews.push("global"); + } + + if (errorKeys.includes("streams")) { + const errorStreamNums = Object.keys(errorsToCheck.streams ?? {}); + const invalidStreamNums = ( + ignoreUntouched ? intersection(errorStreamNums, Object.keys(touched.streams ?? {})) : errorStreamNums + ).map((numString) => Number(numString)); + + invalidViews.push(...invalidStreamNums); + } + + return limitToViews === undefined ? invalidViews : intersection(invalidViews, limitToViews); + }; + + // Returns true if the global config fields or any stream config fields have errors in the provided formik errors, and false otherwise. + // If limitToViews is provided, the error check is limited to only those views. + const hasErrors = (ignoreUntouched: boolean, limitToViews?: BuilderView[]) => { + return invalidViews(ignoreUntouched, limitToViews).length > 0; + }; + + const validateAndTouch = (callback: () => void, limitToViews?: BuilderView[]) => { + validateForm().then((errors) => { + for (const path of Object.keys(flatten(errors))) { + setFieldTouched(path); + } + + // If there are relevant errors, select the erroring view, prioritizing global + // Otherwise, execute the callback. + + const invalidBuilderViews = invalidViews(false, limitToViews, errors); + + if (invalidBuilderViews.length > 0) { + if (invalidBuilderViews.includes("global")) { + setSelectedView("global"); + } else { + setSelectedView(invalidBuilderViews[0]); + setTestStreamIndex(invalidBuilderViews[0] as number); + } + } else { + callback(); + } + }); + }; + + return { hasErrors, validateAndTouch }; +}; diff --git a/airbyte-webapp/src/locales/en.json b/airbyte-webapp/src/locales/en.json index 6278ecd7b740d6..8f4c46bfafddd6 100644 --- a/airbyte-webapp/src/locales/en.json +++ b/airbyte-webapp/src/locales/en.json @@ -623,7 +623,7 @@ "airbyte.datatype.unknown": "Unknown", "airbyte.datatype.boolean": "Boolean", - "connectorBuilder.downloadYaml": "Download YAML", + "connectorBuilder.downloadYaml": "Download Config", "connectorBuilder.testButton": "Test", "connectorBuilder.configMenuTitle": "Configure Test Input", "connectorBuilder.configMenuConfirm": "Confirm", @@ -650,9 +650,11 @@ "connectorBuilder.streamsHeading": "STREAMS ({number})", "connectorBuilder.globalConfiguration": "Global Configuration", "connectorBuilder.noStreamsMessage": "Add a stream to test it here", - "connectorBuilder.toggleModal.text": "Toggling back to the UI will erase any changes you have made in the YAML editor.\n\nIn order to export your current yaml, click the Download YAML button.", + "connectorBuilder.toggleModal.text": "Toggling back to the UI will erase any changes you have made in the YAML editor.\n\nIn order to export your current yaml, click the Download Config button.", "connectorBuilder.toggleModal.title": "Warning", "connectorBuilder.toggleModal.submitButton": "Confirm", + "connectorBuilder.configErrorsDownload": "Cannot download while there are form errors", + "connectorBuilder.configErrorsTest": "Cannot test while there are form errors", "connectorBuilder.connectorImgAlt": "Connector Image", "connectorBuilder.uiYamlToggle.ui": "UI", "connectorBuilder.uiYamlToggle.yaml": "YAML", diff --git a/airbyte-webapp/src/pages/ConnectorBuilderPage/ConnectorBuilderPage.tsx b/airbyte-webapp/src/pages/ConnectorBuilderPage/ConnectorBuilderPage.tsx index cb648dcd5cae23..fc28253ee5c7b4 100644 --- a/airbyte-webapp/src/pages/ConnectorBuilderPage/ConnectorBuilderPage.tsx +++ b/airbyte-webapp/src/pages/ConnectorBuilderPage/ConnectorBuilderPage.tsx @@ -1,10 +1,10 @@ import classnames from "classnames"; import { Formik } from "formik"; import { useIntl } from "react-intl"; -import { useToggle } from "react-use"; import { Builder } from "components/connectorBuilder/Builder/Builder"; import { StreamTestingPanel } from "components/connectorBuilder/StreamTestingPanel"; +import { builderFormValidationSchema } from "components/connectorBuilder/types"; import { YamlEditor } from "components/connectorBuilder/YamlEditor"; import { ResizablePanels } from "components/ui/ResizablePanels"; @@ -17,22 +17,20 @@ import styles from "./ConnectorBuilderPage.module.scss"; const ConnectorBuilderPageInner: React.FC = () => { const { formatMessage } = useIntl(); - const [showYamlEditor, toggleYamlEditor] = useToggle(false); - - const { builderFormValues } = useConnectorBuilderState(); + const { builderFormValues, editorView, setEditorView } = useConnectorBuilderState(); return ( - undefined}> + undefined} validationSchema={builderFormValidationSchema}> {({ values }) => ( - {showYamlEditor ? ( - + {editorView === "yaml" ? ( + setEditorView("ui")} /> ) : ( - + setEditorView("yaml")} /> )} ), diff --git a/airbyte-webapp/src/services/connectorBuilder/ConnectorBuilderStateService.tsx b/airbyte-webapp/src/services/connectorBuilder/ConnectorBuilderStateService.tsx index 3e079cb14a077d..69b01d4e4013e5 100644 --- a/airbyte-webapp/src/services/connectorBuilder/ConnectorBuilderStateService.tsx +++ b/airbyte-webapp/src/services/connectorBuilder/ConnectorBuilderStateService.tsx @@ -26,6 +26,7 @@ const DEFAULT_JSON_MANIFEST_VALUES: ConnectorManifest = { streams: [], }; +type EditorView = "ui" | "yaml"; export type BuilderView = "global" | number; interface Context { @@ -40,6 +41,7 @@ interface Context { selectedView: BuilderView; configString: string; configJson: StreamReadRequestBodyConfig; + editorView: EditorView; setBuilderFormValues: (values: BuilderFormValues) => void; setJsonManifest: (jsonValue: ConnectorManifest) => void; setYamlEditorIsMounted: (value: boolean) => void; @@ -47,6 +49,7 @@ interface Context { setTestStreamIndex: (streamIndex: number) => void; setSelectedView: (view: BuilderView) => void; setConfigString: (configString: string) => void; + setEditorView: (editorView: EditorView) => void; } export const ConnectorBuilderStateContext = React.createContext(null); @@ -79,6 +82,8 @@ export const ConnectorBuilderStateProvider: React.FC("ui"); + // config const [configString, setConfigString] = useState("{\n \n}"); const [configJson, setConfigJson] = useState({}); @@ -129,6 +134,7 @@ export const ConnectorBuilderStateProvider: React.FC{children};