From db5a1491e01570dd740fd2b9a3eb44099392f9f2 Mon Sep 17 00:00:00 2001 From: Edmundo Ruiz Ghanem <168664+edmundito@users.noreply.github.com> Date: Mon, 31 Oct 2022 08:56:43 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=AA=9F=20=F0=9F=8E=89=20Add=20sync=20mode?= =?UTF-8?q?,=20primary=20key,=20cursor=20select=20components=20to=20new=20?= =?UTF-8?q?streams=20table=20=20(#18627)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add SyncModeSelect component Add button type to pill button and stop propagation Stop propagation on popoout outside click listener and migrate to scss module * Update DropDown option to handle click event directly and stop propagation * Add StreamPathSelect component to select the primary key and cursor paths Update PillSelect to handle nil values * Add SyncMode, Primary Key, and Cursor selects to CatalogTreeTableRow * Replace popup click catcher with Overlay component Add Overlay color variant and onClick handler option Fix Overlay import --- .../CatalogTree/next/CatalogTreeTableRow.tsx | 18 +++--- .../CatalogTree/next/StreamDetailsPanel.tsx | 2 +- .../CatalogTree/next/StreamPathSelect.tsx | 64 +++++++++++++++++++ .../next/SyncModeSelect.module.scss | 3 + .../CatalogTree/next/SyncModeSelect.tsx | 44 +++++++++++++ .../ui/DropDown/components/Option.tsx | 15 +++-- .../src/components/ui/Modal/Modal.tsx | 2 +- .../components/ui/Overlay/Overlay.module.scss | 5 +- .../src/components/ui/Overlay/Overlay.tsx | 18 +++++- .../src/components/ui/Overlay/index.ts | 1 + .../components/ui/PillSelect/PillButton.tsx | 2 +- .../components/ui/PillSelect/PillSelect.tsx | 22 +++++-- .../src/components/ui/Popout/Popout.tsx | 20 +++--- 13 files changed, 181 insertions(+), 35 deletions(-) create mode 100644 airbyte-webapp/src/components/connection/CatalogTree/next/StreamPathSelect.tsx create mode 100644 airbyte-webapp/src/components/connection/CatalogTree/next/SyncModeSelect.module.scss create mode 100644 airbyte-webapp/src/components/connection/CatalogTree/next/SyncModeSelect.tsx create mode 100644 airbyte-webapp/src/components/ui/Overlay/index.ts diff --git a/airbyte-webapp/src/components/connection/CatalogTree/next/CatalogTreeTableRow.tsx b/airbyte-webapp/src/components/connection/CatalogTree/next/CatalogTreeTableRow.tsx index c651777b389f..def9f02938b2 100644 --- a/airbyte-webapp/src/components/connection/CatalogTree/next/CatalogTreeTableRow.tsx +++ b/airbyte-webapp/src/components/connection/CatalogTree/next/CatalogTreeTableRow.tsx @@ -1,7 +1,7 @@ import { faArrowRight, faMinus, faPlus } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import classnames from "classnames"; -import { useMemo } from "react"; +import React, { useMemo } from "react"; import { FormattedMessage } from "react-intl"; import { Cell, Row } from "components/SimpleTableComponents"; @@ -10,18 +10,19 @@ import { Switch } from "components/ui/Switch"; import { useBulkEditSelect } from "hooks/services/BulkEdit/BulkEditService"; -import { PathPopout } from "../PathPopout"; import { StreamHeaderProps } from "../StreamHeader"; import { HeaderCell } from "../styles"; import styles from "./CatalogTreeTableRow.module.scss"; +import { StreamPathSelect } from "./StreamPathSelect"; +import { SyncModeSelect } from "./SyncModeSelect"; export const CatalogTreeTableRow: React.FC = ({ stream, destName, destNamespace, - // onSelectSyncMode, + onSelectSyncMode, onSelectStream, - // availableSyncModes, + availableSyncModes, pkType, onPrimaryKeyChange, onCursorChange, @@ -96,29 +97,26 @@ export const CatalogTreeTableRow: React.FC = ({ {syncSchema.syncMode} ) : ( - // TODO: Replace with Dropdown/Popout - syncSchema.syncMode + )} {cursorType && ( - } onPathChange={onCursorChange} /> )} {pkType && ( - } onPathChange={onPrimaryKeyChange} /> )} diff --git a/airbyte-webapp/src/components/connection/CatalogTree/next/StreamDetailsPanel.tsx b/airbyte-webapp/src/components/connection/CatalogTree/next/StreamDetailsPanel.tsx index 3697534e3332..8ad7562424b2 100644 --- a/airbyte-webapp/src/components/connection/CatalogTree/next/StreamDetailsPanel.tsx +++ b/airbyte-webapp/src/components/connection/CatalogTree/next/StreamDetailsPanel.tsx @@ -1,6 +1,6 @@ import { Dialog } from "@headlessui/react"; -import { Overlay } from "components/ui/Overlay/Overlay"; +import { Overlay } from "components/ui/Overlay"; import { AirbyteStream } from "core/request/AirbyteClient"; diff --git a/airbyte-webapp/src/components/connection/CatalogTree/next/StreamPathSelect.tsx b/airbyte-webapp/src/components/connection/CatalogTree/next/StreamPathSelect.tsx new file mode 100644 index 000000000000..1e35e3adae49 --- /dev/null +++ b/airbyte-webapp/src/components/connection/CatalogTree/next/StreamPathSelect.tsx @@ -0,0 +1,64 @@ +import React from "react"; +import { FormattedMessage } from "react-intl"; + +import { PillSelect } from "components/ui/PillSelect"; +import { Tooltip } from "components/ui/Tooltip"; + +import { Path } from "core/domain/catalog"; + +export const pathDisplayName = (path: Path): string => path.join("."); + +export type IndexerType = null | "required" | "sourceDefined"; + +interface StreamPathSelectBaseProps { + paths: Path[]; + pathType: "required" | "sourceDefined"; + placeholder?: React.ReactNode; +} + +interface StreamPathSelectMultiProps { + path?: Path[]; + onPathChange: (pkPath: Path[]) => void; + isMulti: true; +} + +interface StreamPathSelectProps { + path?: Path; + onPathChange: (pkPath: Path) => void; + isMulti?: false; +} + +type PathPopoutProps = StreamPathSelectBaseProps & (StreamPathSelectMultiProps | StreamPathSelectProps); + +export const StreamPathSelect: React.FC = (props) => { + if (props.pathType === "sourceDefined") { + if (props.path && props.path.length > 0) { + const text = props.isMulti ? props.path.map(pathDisplayName).join(", ") : pathDisplayName(props.path); + + return ( + + {text} + + ); + } + + return ; + } + + const options = props.paths.map((path) => ({ + value: path, + label: pathDisplayName(path), + })); + + return ( + : { value: Path }) => { + const finalValues = Array.isArray(options) ? options.map((op) => op.value) : options.value; + props.onPathChange(finalValues); + }} + /> + ); +}; diff --git a/airbyte-webapp/src/components/connection/CatalogTree/next/SyncModeSelect.module.scss b/airbyte-webapp/src/components/connection/CatalogTree/next/SyncModeSelect.module.scss new file mode 100644 index 000000000000..af40c11fd767 --- /dev/null +++ b/airbyte-webapp/src/components/connection/CatalogTree/next/SyncModeSelect.module.scss @@ -0,0 +1,3 @@ +.pillSelect { + width: 100%; +} diff --git a/airbyte-webapp/src/components/connection/CatalogTree/next/SyncModeSelect.tsx b/airbyte-webapp/src/components/connection/CatalogTree/next/SyncModeSelect.tsx new file mode 100644 index 000000000000..9cc8e7756821 --- /dev/null +++ b/airbyte-webapp/src/components/connection/CatalogTree/next/SyncModeSelect.tsx @@ -0,0 +1,44 @@ +import { useMemo } from "react"; +import { FormattedMessage } from "react-intl"; + +import { DropDownOptionDataItem } from "components/ui/DropDown"; +import { PillSelect } from "components/ui/PillSelect"; + +import { DestinationSyncMode, SyncMode } from "core/request/AirbyteClient"; + +import styles from "./SyncModeSelect.module.scss"; + +interface SyncModeValue { + syncMode: SyncMode; + destinationSyncMode: DestinationSyncMode; +} + +interface SyncModeOption { + value: SyncModeValue; +} + +interface SyncModeSelectProps { + options: SyncModeOption[]; + value: Partial; + onChange?: (option: DropDownOptionDataItem) => void; +} + +export const SyncModeSelect: React.FC = ({ options, onChange, value }) => { + const pillSelectOptions = useMemo(() => { + return options.map(({ value }) => { + const { syncMode, destinationSyncMode } = value; + return { + label: ( + <> + + {` | `} + + + ), + value, + }; + }); + }, [options]); + + return ; +}; diff --git a/airbyte-webapp/src/components/ui/DropDown/components/Option.tsx b/airbyte-webapp/src/components/ui/DropDown/components/Option.tsx index 175d7669b676..281a00674486 100644 --- a/airbyte-webapp/src/components/ui/DropDown/components/Option.tsx +++ b/airbyte-webapp/src/components/ui/DropDown/components/Option.tsx @@ -11,17 +11,16 @@ export type DropDownOptionProps = { data: { disabled: boolean; index: number; fullText?: boolean } & DropDownOptionDataItem; } & OptionProps; -export interface DropDownOptionDataItem { +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export interface DropDownOptionDataItem { label?: string; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - value?: any; + value?: Value; groupValue?: string; groupValueText?: string; img?: React.ReactNode; primary?: boolean; secondary?: boolean; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - config?: any; + config?: Config; } export const OptionView = styled.div<{ @@ -61,6 +60,12 @@ export const DropDownOption: React.FC = (props) => { isSelected={props.isSelected && !props.isMulti} isDisabled={props.isDisabled} isFocused={props.isFocused} + onClick={(event) => { + // This custom onClick handler prevents the click event from bubbling up outside of the option + // for cases where the Dropdown is a child of a clickable parent such as a table row. + props.selectOption(props.data); + event.stopPropagation(); + }} > {props.isMulti && ( diff --git a/airbyte-webapp/src/components/ui/Modal/Modal.tsx b/airbyte-webapp/src/components/ui/Modal/Modal.tsx index 942dadc3ac0b..a597ebc2a17c 100644 --- a/airbyte-webapp/src/components/ui/Modal/Modal.tsx +++ b/airbyte-webapp/src/components/ui/Modal/Modal.tsx @@ -3,7 +3,7 @@ import classNames from "classnames"; import React, { useState } from "react"; import { Card } from "../Card"; -import { Overlay } from "../Overlay/Overlay"; +import { Overlay } from "../Overlay"; import styles from "./Modal.module.scss"; export interface ModalProps { diff --git a/airbyte-webapp/src/components/ui/Overlay/Overlay.module.scss b/airbyte-webapp/src/components/ui/Overlay/Overlay.module.scss index 0102f7db41b6..e1d791597f27 100644 --- a/airbyte-webapp/src/components/ui/Overlay/Overlay.module.scss +++ b/airbyte-webapp/src/components/ui/Overlay/Overlay.module.scss @@ -6,5 +6,8 @@ left: 0; right: 0; bottom: 0; - background: rgba(colors.$black, 0.5); + + &.dark { + background: rgba(colors.$black, 0.5); + } } diff --git a/airbyte-webapp/src/components/ui/Overlay/Overlay.tsx b/airbyte-webapp/src/components/ui/Overlay/Overlay.tsx index 1d9bdea7f3b8..78508382c510 100644 --- a/airbyte-webapp/src/components/ui/Overlay/Overlay.tsx +++ b/airbyte-webapp/src/components/ui/Overlay/Overlay.tsx @@ -1,3 +1,19 @@ +import classNames from "classnames"; + import styles from "./Overlay.module.scss"; -export const Overlay: React.FC = () =>