From 9dd03566b1c836832fbdeca5099e79fad664e72b Mon Sep 17 00:00:00 2001
From: Anton Kulyk <kuliks.anton@gmail.com>
Date: Wed, 21 Sep 2022 18:07:40 +0100
Subject: [PATCH] Extract `DraggableSidebarLink` component (#25457)

* Export `SidebarLinkProps`

* Expose `SidebarLink's` left & right containers

That'd allow styling left and right elements from external components

* Extract `DraggableSidebarLink` component

Based on sortable bookmarks code

* Use `DraggableSidebarLink` in `BookmarkList`

* Fix dragged item styles
---
 .../BookmarkList/BookmarkList.styled.tsx      | 41 +----------------
 .../BookmarkList/BookmarkList.tsx             |  7 +--
 .../DraggableSidebarLink.styled.tsx           | 45 +++++++++++++++++++
 .../SidebarItems/DraggableSidebarLink.tsx     | 21 +++++++++
 .../SidebarItems/SidebarItems.styled.tsx      |  3 ++
 .../MainNavbar/SidebarItems/SidebarLink.tsx   | 19 ++++++--
 .../MainNavbar/SidebarItems/index.ts          |  1 +
 .../sortable.css                              |  3 +-
 8 files changed, 90 insertions(+), 50 deletions(-)
 create mode 100644 frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/DraggableSidebarLink.styled.tsx
 create mode 100644 frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/DraggableSidebarLink.tsx
 rename frontend/src/metabase/nav/containers/MainNavbar/{MainNavbarContainer/BookmarkList => SidebarItems}/sortable.css (67%)

diff --git a/frontend/src/metabase/nav/containers/MainNavbar/MainNavbarContainer/BookmarkList/BookmarkList.styled.tsx b/frontend/src/metabase/nav/containers/MainNavbar/MainNavbarContainer/BookmarkList/BookmarkList.styled.tsx
index 4f76a194448..cf891ff2512 100644
--- a/frontend/src/metabase/nav/containers/MainNavbar/MainNavbarContainer/BookmarkList/BookmarkList.styled.tsx
+++ b/frontend/src/metabase/nav/containers/MainNavbar/MainNavbarContainer/BookmarkList/BookmarkList.styled.tsx
@@ -1,28 +1,11 @@
 import styled from "@emotion/styled";
-import { css } from "@emotion/react";
 
 import { color } from "metabase/lib/colors";
 
-import Icon from "metabase/components/Icon";
+import { DraggableSidebarLink } from "../../SidebarItems";
 
-import { SidebarLink } from "../../SidebarItems";
-
-type SidebarBookmarkItem = {
-  isSorting: boolean;
-};
-
-export const DragIcon = styled(Icon)`
-  left: 2px;
-  opacity: 0;
-  position: absolute;
-  top: 50%;
-  transform: translateY(-50%);
-  cursor: grab;
-`;
-
-export const SidebarBookmarkItem = styled(SidebarLink)<SidebarBookmarkItem>`
+export const SidebarBookmarkItem = styled(DraggableSidebarLink)`
   padding-left: 0.75rem;
-  position: relative;
 
   &:hover {
     button {
@@ -33,10 +16,6 @@ export const SidebarBookmarkItem = styled(SidebarLink)<SidebarBookmarkItem>`
         outline: none;
       }
     }
-
-    > svg {
-      opacity: 0.3;
-    }
   }
 
   button {
@@ -50,20 +29,4 @@ export const SidebarBookmarkItem = styled(SidebarLink)<SidebarBookmarkItem>`
       outline: none;
     }
   }
-
-  ${props =>
-    props.isSorting &&
-    css`
-      &:hover {
-        background: white;
-
-        svg {
-          color: ${color("brand-light")} !important;
-        }
-
-        button {
-          opacity: 0;
-        }
-      }
-    `}
 `;
diff --git a/frontend/src/metabase/nav/containers/MainNavbar/MainNavbarContainer/BookmarkList/BookmarkList.tsx b/frontend/src/metabase/nav/containers/MainNavbar/MainNavbarContainer/BookmarkList/BookmarkList.tsx
index 1f4f9dc23ee..3da21b7d89a 100644
--- a/frontend/src/metabase/nav/containers/MainNavbar/MainNavbarContainer/BookmarkList/BookmarkList.tsx
+++ b/frontend/src/metabase/nav/containers/MainNavbar/MainNavbarContainer/BookmarkList/BookmarkList.tsx
@@ -2,8 +2,6 @@ import React, { useCallback, useEffect, useState } from "react";
 import { t } from "ttag";
 import { connect } from "react-redux";
 
-import "./sortable.css";
-
 import {
   SortableContainer,
   SortableElement,
@@ -20,7 +18,7 @@ import * as Urls from "metabase/lib/urls";
 import { SelectedItem } from "../../types";
 import { SidebarHeading } from "../../MainNavbar.styled";
 
-import { DragIcon, SidebarBookmarkItem } from "./BookmarkList.styled";
+import { SidebarBookmarkItem } from "./BookmarkList.styled";
 
 const mapDispatchToProps = {
   onDeleteBookmark: ({ item_id, type }: Bookmark) =>
@@ -91,10 +89,9 @@ const BookmarkItem = ({
         url={url}
         icon={icon}
         isSelected={isSelected}
-        isSorting={isSorting}
+        isDragging={isSorting}
         hasDefaultIconStyle={!isIrregularCollection}
         onClick={onSelect}
-        left={<DragIcon name="grabber2" size={12} />}
         right={
           <button onClick={onRemove}>
             <Tooltip tooltip={t`Remove bookmark`} placement="bottom">
diff --git a/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/DraggableSidebarLink.styled.tsx b/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/DraggableSidebarLink.styled.tsx
new file mode 100644
index 00000000000..50f3b0bee2c
--- /dev/null
+++ b/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/DraggableSidebarLink.styled.tsx
@@ -0,0 +1,45 @@
+import styled from "@emotion/styled";
+import { css } from "@emotion/react";
+
+import Icon from "metabase/components/Icon";
+
+import { color } from "metabase/lib/colors";
+
+import SidebarLink from "./SidebarLink";
+
+export const DragIcon = styled(Icon)`
+  left: 2px;
+  opacity: 0;
+  position: absolute;
+  top: 50%;
+  transform: translateY(-50%);
+  cursor: grab;
+`;
+
+// Notice that dragged item styles are defined in sortable.css file
+// This is a limitation of react-sortable-hoc library
+export const StyledSidebarLink = styled(SidebarLink)<{ isDragging: boolean }>`
+  position: relative;
+
+  &:hover {
+    ${DragIcon} {
+      opacity: 0.3;
+    }
+  }
+
+  ${props =>
+    props.isDragging &&
+    css`
+      &:hover {
+        background: ${color("bg-white")};
+
+        ${SidebarLink.Icon}, ${DragIcon} {
+          color: ${color("brand-light")} !important;
+        }
+
+        ${SidebarLink.RightElement} {
+          opacity: 0;
+        }
+      }
+    `}
+`;
diff --git a/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/DraggableSidebarLink.tsx b/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/DraggableSidebarLink.tsx
new file mode 100644
index 00000000000..42106f8d322
--- /dev/null
+++ b/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/DraggableSidebarLink.tsx
@@ -0,0 +1,21 @@
+import React from "react";
+
+import { SidebarLinkProps } from "./SidebarLink";
+import { DragIcon, StyledSidebarLink } from "./DraggableSidebarLink.styled";
+
+import "./sortable.css";
+
+interface Props extends Omit<SidebarLinkProps, "left"> {
+  isDragging: boolean;
+}
+
+function DraggableSidebarLink(props: Props) {
+  return (
+    <StyledSidebarLink
+      {...props}
+      left={<DragIcon name="grabber2" size={12} />}
+    />
+  );
+}
+
+export default DraggableSidebarLink;
diff --git a/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/SidebarItems.styled.tsx b/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/SidebarItems.styled.tsx
index 3eec95ba5fd..0153ba7e7da 100644
--- a/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/SidebarItems.styled.tsx
+++ b/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/SidebarItems.styled.tsx
@@ -127,3 +127,6 @@ export function NameContainer({ children: itemName }: { children: string }) {
   }
   return <TreeNode.NameContainer>{itemName}</TreeNode.NameContainer>;
 }
+
+export const LeftElementContainer = styled.div``;
+export const RightElementContainer = styled.div``;
diff --git a/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/SidebarLink.tsx b/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/SidebarLink.tsx
index dacbbef2e91..af0a7e38e41 100644
--- a/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/SidebarLink.tsx
+++ b/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/SidebarLink.tsx
@@ -11,9 +11,11 @@ import {
   NodeRoot,
   SidebarIcon,
   FullWidthButton,
+  LeftElementContainer,
+  RightElementContainer,
 } from "./SidebarItems.styled";
 
-interface Props {
+interface SidebarLinkProps {
   children: string;
   url?: string;
   icon?: string | IconProps | React.ReactElement;
@@ -51,7 +53,7 @@ function SidebarLink({
   left = null,
   right = null,
   ...props
-}: Props) {
+}: SidebarLinkProps) {
   const renderIcon = useCallback(() => {
     if (!icon) {
       return null;
@@ -83,16 +85,25 @@ function SidebarLink({
       onMouseDown={disableImageDragging}
       {...props}
     >
-      {React.isValidElement(left) && left}
+      {React.isValidElement(left) && (
+        <LeftElementContainer>{left}</LeftElementContainer>
+      )}
       <Content>
         {icon && renderIcon()}
         <NameContainer>{children}</NameContainer>
       </Content>
-      {React.isValidElement(right) && right}
+      {React.isValidElement(right) && (
+        <RightElementContainer>{right}</RightElementContainer>
+      )}
     </NodeRoot>
   );
 }
 
+export type { SidebarLinkProps };
+
 export default Object.assign(SidebarLink, {
   NameContainers: [ItemName, TreeNode.NameContainer],
+  Icon: SidebarIcon,
+  LeftElement: LeftElementContainer,
+  RightElement: RightElementContainer,
 });
diff --git a/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/index.ts b/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/index.ts
index f97e8e7a976..8ce9b417f03 100644
--- a/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/index.ts
+++ b/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/index.ts
@@ -1,3 +1,4 @@
+export { default as DraggableSidebarLink } from "./DraggableSidebarLink";
 export { default as SidebarCollectionLink } from "./SidebarCollectionLink";
 export { default as SidebarDataAppLink } from "./SidebarDataAppLink";
 export { default as SidebarLink } from "./SidebarLink";
diff --git a/frontend/src/metabase/nav/containers/MainNavbar/MainNavbarContainer/BookmarkList/sortable.css b/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/sortable.css
similarity index 67%
rename from frontend/src/metabase/nav/containers/MainNavbar/MainNavbarContainer/BookmarkList/sortable.css
rename to frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/sortable.css
index 68bb7d49aaf..181c075c2fb 100644
--- a/frontend/src/metabase/nav/containers/MainNavbar/MainNavbarContainer/BookmarkList/sortable.css
+++ b/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/sortable.css
@@ -1,9 +1,8 @@
 .react-sortable-hoc-helper.sorting {
-  background: white;
   border-bottom: 2px solid var(--color-brand);
 }
 
-.react-sortable-hoc-helper.sorting > svg {
+.react-sortable-hoc-helper.sorting .Icon-grabber2 {
   color: var(--color-brand);
   opacity: 0.5;
 }
-- 
GitLab