diff --git a/airbyte-webapp/.eslintrc b/airbyte-webapp/.eslintrc index 1d7be5be20c5..42ab74d3eb6d 100644 --- a/airbyte-webapp/.eslintrc +++ b/airbyte-webapp/.eslintrc @@ -19,7 +19,7 @@ "rules": { "jsx-a11y/label-has-associated-control": "error", "curly": "warn", - "css-modules/no-undef-class": ["error", { "camelCase": true }], + "css-modules/no-undef-class": "off", "css-modules/no-unused-class": ["error", { "camelCase": true }], "dot-location": "warn", "dot-notation": "warn", diff --git a/airbyte-webapp/src/packages/cloud/views/layout/SideBar/SideBar.module.scss b/airbyte-webapp/src/packages/cloud/views/layout/SideBar/SideBar.module.scss new file mode 100644 index 000000000000..bd226e0c8703 --- /dev/null +++ b/airbyte-webapp/src/packages/cloud/views/layout/SideBar/SideBar.module.scss @@ -0,0 +1,26 @@ +@use "../../../../../scss/colors"; +@use "../../../../../scss/variables"; +@forward "../../../../../views/layout/SideBar/SideBar.module.scss"; + +$sidebar-workspace-font-size: 9px; +$sidebar-workspace-font-weight: 400; + +.workspaceButton { + overflow: hidden; + cursor: pointer; + display: block; + height: 21px; + width: 100%; + border: 0; + border-radius: variables.$border-radius-md; + background-color: rgba(255, 255, 255, 20%); + margin-top: variables.$spacing-md; + padding: 0 variables.$spacing-md; + font-size: $sidebar-workspace-font-size; + font-weight: $sidebar-workspace-font-weight; + color: colors.$white; + white-space: nowrap; + text-overflow: ellipsis; + text-align: center; + outline: none; +} diff --git a/airbyte-webapp/src/packages/cloud/views/layout/SideBar/SideBar.tsx b/airbyte-webapp/src/packages/cloud/views/layout/SideBar/SideBar.tsx index 887bd0438fbb..04ae79a6e104 100644 --- a/airbyte-webapp/src/packages/cloud/views/layout/SideBar/SideBar.tsx +++ b/airbyte-webapp/src/packages/cloud/views/layout/SideBar/SideBar.tsx @@ -1,13 +1,16 @@ -import { faQuestionCircle } from "@fortawesome/free-regular-svg-icons"; +import { faSlack } from "@fortawesome/free-brands-svg-icons"; +import { faEnvelope } from "@fortawesome/free-regular-svg-icons"; +import { faQuestionCircle } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import React from "react"; import { FormattedMessage, FormattedNumber } from "react-intl"; import { NavLink } from "react-router-dom"; -import styled from "styled-components"; import { Link } from "components"; +import { Text } from "components/base/Text"; import { CreditsIcon } from "components/icons/CreditsIcon"; +import { useConfig } from "config"; import { FeatureItem, IfFeatureEnabled } from "hooks/services/Feature"; import { useCurrentWorkspace } from "hooks/services/useWorkspace"; import { CloudRoutes } from "packages/cloud/cloudRoutes"; @@ -19,79 +22,44 @@ import ConnectionsIcon from "views/layout/SideBar/components/ConnectionsIcon"; import DestinationIcon from "views/layout/SideBar/components/DestinationIcon"; import DocsIcon from "views/layout/SideBar/components/DocsIcon"; import OnboardingIcon from "views/layout/SideBar/components/OnboardingIcon"; +import RecipesIcon from "views/layout/SideBar/components/RecipesIcon"; import SettingsIcon from "views/layout/SideBar/components/SettingsIcon"; -import SidebarPopout, { Icon, Item } from "views/layout/SideBar/components/SidebarPopout"; +import { SidebarDropdownMenu, SidebarDropdownMenuItemType } from "views/layout/SideBar/components/SidebarDropdownMenu"; import SourceIcon from "views/layout/SideBar/components/SourceIcon"; +import StatusIcon from "views/layout/SideBar/components/StatusIcon"; import { NotificationIndicator } from "views/layout/SideBar/NotificationIndicator"; -import { useCalculateSidebarStyles, getPopoutStyles } from "views/layout/SideBar/SideBar"; +import { useCalculateSidebarStyles } from "views/layout/SideBar/SideBar"; import { RoutePaths } from "../../../../../pages/routePaths"; - -const Bar = styled.nav` - width: 100px; - min-width: 65px; - height: 100%; - background: ${({ theme }) => theme.darkPrimaryColor}; - padding: 23px 3px 15px 4px; - text-align: center; - display: flex; - flex-direction: column; - justify-content: space-between; - position: relative; - z-index: 9999; -`; - -const Menu = styled.ul` - padding: 0; - margin: 20px 0 0; - width: 100%; -`; - -const Text = styled.div` - margin-top: 7px; -`; - -const WorkspaceButton = styled.div` - font-size: 9px; - line-height: 21px; - font-weight: 400; - height: 21px; - color: ${({ theme }) => theme.whiteColor}; - border-radius: 10px; - margin-top: 13px; - background: rgba(255, 255, 255, 0.2); - cursor: pointer; - display: block; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - padding: 0 8px; - text-align: center; -`; +import styles from "./SideBar.module.scss"; const SideBar: React.FC = () => { + const navLinkClassName = useCalculateSidebarStyles(); const workspace = useCurrentWorkspace(); const cloudWorkspace = useGetCloudWorkspace(workspace.workspaceId); + const config = useConfig(); const { show } = useIntercom(); const handleChatUs = () => show(); - const navLinkClassName = useCalculateSidebarStyles(); - return ( - + ); }; diff --git a/airbyte-webapp/src/views/Connection/CatalogDiffModal/components/DiffAccordionHeader.tsx b/airbyte-webapp/src/views/Connection/CatalogDiffModal/components/DiffAccordionHeader.tsx index cca15b62d5cc..33d74bca7eb0 100644 --- a/airbyte-webapp/src/views/Connection/CatalogDiffModal/components/DiffAccordionHeader.tsx +++ b/airbyte-webapp/src/views/Connection/CatalogDiffModal/components/DiffAccordionHeader.tsx @@ -25,10 +25,8 @@ export const DiffAccordionHeader: React.FC = ({ newCount, changedCount, }) => { - // eslint-disable-next-line css-modules/no-undef-class const nameCellStyle = classnames(styles.nameCell, styles.row); - // eslint-disable-next-line css-modules/no-undef-class const namespaceCellStyles = classnames(styles.nameCell, styles.row, styles.namespace); const { formatMessage } = useIntl(); diff --git a/airbyte-webapp/src/views/Connection/CatalogDiffModal/components/FieldSection.tsx b/airbyte-webapp/src/views/Connection/CatalogDiffModal/components/FieldSection.tsx index f6d150fb80b1..9d85aa78a781 100644 --- a/airbyte-webapp/src/views/Connection/CatalogDiffModal/components/FieldSection.tsx +++ b/airbyte-webapp/src/views/Connection/CatalogDiffModal/components/FieldSection.tsx @@ -15,9 +15,7 @@ interface FieldSectionProps { export const FieldSection: React.FC = ({ streams, diffVerb }) => { const { formatMessage } = useIntl(); return ( - // eslint-disable-next-line css-modules/no-undef-class
- {/* eslint-disable-next-line css-modules/no-undef-class */}
diff --git a/airbyte-webapp/src/views/Connection/CatalogTree/StreamHeader.tsx b/airbyte-webapp/src/views/Connection/CatalogTree/StreamHeader.tsx index 4b68734658e0..01606ac001db 100644 --- a/airbyte-webapp/src/views/Connection/CatalogTree/StreamHeader.tsx +++ b/airbyte-webapp/src/views/Connection/CatalogTree/StreamHeader.tsx @@ -94,8 +94,6 @@ export const StreamHeader: React.FC = ({ [styles.purpleBackground]: isSelected, [styles.redBorder]: hasError, }); - // FIXME: find out why checkboxCell warns as unused - // eslint-disable-next-line css-modules/no-undef-class const checkboxCellCustomStyle = classnames(styles.checkboxCell, { [styles.streamRowCheckboxCell]: true }); return ( diff --git a/airbyte-webapp/src/views/layout/SideBar/SideBar.module.scss b/airbyte-webapp/src/views/layout/SideBar/SideBar.module.scss index 4321f1f01f01..2bcbe139f03f 100644 --- a/airbyte-webapp/src/views/layout/SideBar/SideBar.module.scss +++ b/airbyte-webapp/src/views/layout/SideBar/SideBar.module.scss @@ -1,5 +1,40 @@ @use "../../../scss/colors"; @use "../../../scss/variables"; +@use "../../../scss/z-indices"; + +.nav { + z-index: z-indices.$sidebar; + display: flex; + position: relative; + flex-direction: column; + justify-content: space-between; + height: 100%; + width: 100px; + min-width: 65px; + background-color: colors.$dark-blue; + padding: 23px 4px 15px; + text-align: center; +} + +.text { + margin-top: 7px; + color: colors.$white; +} + +.helpIcon { + font-size: 21px; + line-height: 21px; +} + +.menu { + width: 100%; + padding: 0; + margin: variables.$spacing-xl 0 0; + + li { + margin-top: 7px; + } +} .menuItem { background-color: transparent; @@ -17,7 +52,6 @@ font-weight: normal; font-size: 12px; line-height: 15px; - margin-top: 7px; text-decoration: none; position: relative; @@ -50,9 +84,4 @@ background: colors.$dark-blue-400; } } - - &.popoutOpen { - background: colors.$dark-blue-600; - transition: variables.$transition-out; - } } diff --git a/airbyte-webapp/src/views/layout/SideBar/SideBar.tsx b/airbyte-webapp/src/views/layout/SideBar/SideBar.tsx index b9e5dce9a963..2c9dbc430c91 100644 --- a/airbyte-webapp/src/views/layout/SideBar/SideBar.tsx +++ b/airbyte-webapp/src/views/layout/SideBar/SideBar.tsx @@ -1,12 +1,13 @@ +import { faSlack } from "@fortawesome/free-brands-svg-icons"; import { faRocket } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import classnames from "classnames"; import React from "react"; import { FormattedMessage } from "react-intl"; import { NavLink, useLocation } from "react-router-dom"; -import styled from "styled-components"; import { Link } from "components"; +import { Text } from "components/base/Text"; import Version from "components/Version"; import { useConfig } from "config"; @@ -17,41 +18,13 @@ import ConnectionsIcon from "./components/ConnectionsIcon"; import DestinationIcon from "./components/DestinationIcon"; import DocsIcon from "./components/DocsIcon"; import OnboardingIcon from "./components/OnboardingIcon"; +import RecipesIcon from "./components/RecipesIcon"; import SettingsIcon from "./components/SettingsIcon"; -import SidebarPopout from "./components/SidebarPopout"; +import { SidebarDropdownMenu, SidebarDropdownMenuItemType } from "./components/SidebarDropdownMenu"; import SourceIcon from "./components/SourceIcon"; import { NotificationIndicator } from "./NotificationIndicator"; import styles from "./SideBar.module.scss"; -const Bar = styled.nav` - width: 100px; - min-width: 65px; - height: 100%; - background: ${({ theme }) => theme.darkPrimaryColor}; - padding: 23px 3px 15px 4px; - text-align: center; - display: flex; - flex-direction: column; - justify-content: space-between; - position: relative; - z-index: 9999; -`; - -const Menu = styled.ul` - padding: 0; - margin: 20px 0 0; - width: 100%; -`; - -const Text = styled.div` - margin-top: 7px; -`; - -const HelpIcon = styled(FontAwesomeIcon)` - font-size: 21px; - line-height: 21px; -`; - export const useCalculateSidebarStyles = () => { const location = useLocation(); @@ -63,28 +36,23 @@ export const useCalculateSidebarStyles = () => { return ({ isActive }: { isActive: boolean }) => menuItemStyle(isActive); }; -export const getPopoutStyles = (isOpen?: boolean) => { - return classnames(styles.menuItem, { [styles.popoutOpen]: isOpen }); -}; - const SideBar: React.FC = () => { const config = useConfig(); const workspace = useCurrentWorkspace(); - const navLinkClassName = useCalculateSidebarStyles(); return ( - + ); }; diff --git a/airbyte-webapp/src/views/layout/SideBar/components/SidebarDropdownMenu.module.scss b/airbyte-webapp/src/views/layout/SideBar/components/SidebarDropdownMenu.module.scss new file mode 100644 index 000000000000..82b425ddf821 --- /dev/null +++ b/airbyte-webapp/src/views/layout/SideBar/components/SidebarDropdownMenu.module.scss @@ -0,0 +1,77 @@ +@use "../../../../scss/colors"; +@use "../../../../scss/variables"; + +.sidebarMenu { + position: relative; +} + +.button { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 70px; + width: 100%; + background: transparent; + border: 0; + outline: none; + cursor: pointer; + border-radius: variables.$border-radius-md; + color: colors.$grey-30; + text-decoration: none; + transition: variables.$transition-out; + + .text { + margin-top: 7px; + color: colors.$grey-30; + } + + &.open { + background-color: colors.$dark-blue-600; + } + + &:hover:not(.open) { + background-color: colors.$dark-blue-800; + } +} + +.items { + position: absolute; + left: 100%; + bottom: 0; + min-width: 260px; + margin: 0 0 14px 12px; + padding: variables.$spacing-sm 0; + outline: none; + border-radius: variables.$border-radius-xs; + background-color: colors.$white; + box-shadow: 0 8px 10px 0 rgb(11 10 26 / 4%), 0 3px 14px 0 rgb(11 10 26 / 8%), 0 5px 5px 0 rgb(11 10 26 / 12%); + + &:focus-within { + outline: none; + } + + .item { + cursor: pointer; + display: flex; + align-items: center; + height: 42px; + width: 100%; + padding: 0 variables.$spacing-lg; + border: 0; + background-color: transparent; + text-decoration: none; + + .icon { + display: flex; + align-items: center; + width: 34px; + font-size: 22px; + color: colors.$dark-blue; + } + + &.active { + background-color: colors.$grey-50; + } + } +} diff --git a/airbyte-webapp/src/views/layout/SideBar/components/SidebarDropdownMenu.tsx b/airbyte-webapp/src/views/layout/SideBar/components/SidebarDropdownMenu.tsx new file mode 100644 index 000000000000..6ce486eb741b --- /dev/null +++ b/airbyte-webapp/src/views/layout/SideBar/components/SidebarDropdownMenu.tsx @@ -0,0 +1,80 @@ +import { Menu } from "@headlessui/react"; +import classNames from "classnames"; +import React from "react"; + +import { Text } from "components/base/Text"; + +import styles from "./SidebarDropdownMenu.module.scss"; + +export enum SidebarDropdownMenuItemType { + LINK = "link", + BUTTON = "button", +} + +interface MenuItemLink { + type: SidebarDropdownMenuItemType.LINK; + href: string; + icon: React.ReactNode; + displayName: React.ReactNode; +} + +interface MenuItemButton { + type: SidebarDropdownMenuItemType.BUTTON; + icon: React.ReactNode; + displayName: React.ReactNode; + onClick: () => void; +} + +interface Label { + icon: React.ReactNode; + displayName: React.ReactNode; +} + +export const SidebarDropdownMenu: React.FC<{ + label: Label; + options?: Array; +}> = ({ label, options }) => { + function menuItem(active: boolean, item: MenuItemLink | MenuItemButton): React.ReactNode { + switch (item.type) { + case SidebarDropdownMenuItemType.LINK: + return ( + + {item.icon} + {item.displayName} + + ); + case SidebarDropdownMenuItemType.BUTTON: + return ( + + ); + } + } + + return ( + + {({ open }) => ( + <> + + {label.icon} + + {label.displayName} + + + + {options?.map((item, index) => ( + {({ active }) => menuItem(active, item)} + ))} + + + )} + + ); +}; diff --git a/airbyte-webapp/src/views/layout/SideBar/components/SidebarPopout.tsx b/airbyte-webapp/src/views/layout/SideBar/components/SidebarPopout.tsx deleted file mode 100644 index 842732987692..000000000000 --- a/airbyte-webapp/src/views/layout/SideBar/components/SidebarPopout.tsx +++ /dev/null @@ -1,132 +0,0 @@ -import { faSlack } from "@fortawesome/free-brands-svg-icons"; -import { faEnvelope } from "@fortawesome/free-regular-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import React, { useMemo } from "react"; -import { FormattedMessage } from "react-intl"; -import styled from "styled-components"; - -import { Popout } from "components"; - -import { useConfig } from "config"; - -import DocsIcon from "./DocsIcon"; -import RecipesIcon from "./RecipesIcon"; -import StatusIcon from "./StatusIcon"; - -export const Item = styled.a` - display: flex; - flex-direction: row; - align-items: center; - text-decoration: none; - color: ${({ theme }) => theme.textColor}; - font-size: 14px; - font-weight: 500; -`; - -export const Icon = styled.div` - width: 34px; - font-size: 22px; -`; - -const SidebarPopout: React.FC<{ - children: (props: { onOpen: () => void; isOpen?: boolean }) => React.ReactNode; - options: Array<{ value: string; label?: React.ReactNode }>; -}> = ({ children, options }) => { - const config = useConfig(); - - const listData = useMemo( - () => - options.map((item) => { - switch (item.value) { - case "docs": - return { - value: "docs", - label: ( - - - - - - - ), - }; - case "slack": - return { - value: "slack", - label: ( - - - - - - - ), - }; - case "status": - return { - value: "status", - label: ( - - - - - - - ), - }; - case "recipes": - return { - value: "recipes", - label: ( - - - - - - - ), - }; - case "ticket": - return { - value: "ticket", - label: ( - - - - - - - ), - }; - default: - return { - value: item.value, - label: item.label ?? item.value, - }; - } - }), - [options, config] - ); - - return ( - - children({ - onOpen: targetProps.onOpen, - isOpen: targetProps.isOpen, - }) - } - styles={{ - menuPortal: (base) => ({ - ...base, - // TODO: temporary dirty hack - transform: "translate3D(100px, -100px, 0px)", - }), - }} - isSearchable={false} - options={listData} - /> - ); -}; - -export default SidebarPopout;