From c0ef832c8cd117f83680e0ff01774b6c4261d70d Mon Sep 17 00:00:00 2001
From: Alexander Polyankin <alexander.polyankin@metabase.com>
Date: Wed, 15 Jun 2022 11:51:07 +0300
Subject: [PATCH] Show disabled menu item to show visualization (#23339)

---
 .../components/ActionMenu/ActionMenu.tsx      | 13 ++++++------
 .../PinnedItemOverview/PinnedItemOverview.tsx |  4 ++--
 .../PinnedQuestionCard/PinnedQuestionCard.tsx |  4 ++--
 frontend/src/metabase/collections/utils.ts    |  6 ++----
 .../src/metabase/components/EntityItem.jsx    | 11 ++++++----
 .../src/metabase/components/EntityMenu.jsx    |  1 +
 .../metabase/components/EntityMenuItem.jsx    | 21 ++++++++++++++-----
 .../components/EntityMenuItem.styled.jsx      | 10 ++++-----
 8 files changed, 41 insertions(+), 29 deletions(-)

diff --git a/frontend/src/metabase/collections/components/ActionMenu/ActionMenu.tsx b/frontend/src/metabase/collections/components/ActionMenu/ActionMenu.tsx
index ecfcf20b301..49f37a2cc13 100644
--- a/frontend/src/metabase/collections/components/ActionMenu/ActionMenu.tsx
+++ b/frontend/src/metabase/collections/components/ActionMenu/ActionMenu.tsx
@@ -5,6 +5,7 @@ import { ANALYTICS_CONTEXT } from "metabase/collections/constants";
 import {
   hasRequiredParameters,
   isItemPinned,
+  isPreviewShown,
   isPreviewEnabled,
   Item,
 } from "metabase/collections/utils";
@@ -50,11 +51,8 @@ function ActionMenu({
   deleteBookmark,
 }: ActionMenuProps) {
   const isBookmarked = bookmarks && getIsBookmarked(item, bookmarks);
-  const canTogglePreview =
-    isItemPinned(item) &&
-    hasRequiredParameters(item) &&
-    collection.can_write &&
-    item.setCollectionPreview;
+  const isPreviewOptionShown =
+    isItemPinned(item) && collection.can_write && item.setCollectionPreview;
 
   const handlePin = useCallback(() => {
     item.setPinned(!isItemPinned(item));
@@ -89,7 +87,8 @@ function ActionMenu({
         className={className}
         item={item}
         isBookmarked={isBookmarked}
-        isPreviewEnabled={isPreviewEnabled(item)}
+        isPreviewShown={isPreviewShown(item)}
+        isPreviewAvailable={hasRequiredParameters(item)}
         onPin={collection.can_write ? handlePin : null}
         onMove={collection.can_write && item.setCollection ? handleMove : null}
         onCopy={item.copy ? handleCopy : null}
@@ -97,7 +96,7 @@ function ActionMenu({
           collection.can_write && item.setArchived ? handleArchive : null
         }
         onToggleBookmark={handleToggleBookmark}
-        onTogglePreview={canTogglePreview ? handleTogglePreview : null}
+        onTogglePreview={isPreviewOptionShown ? handleTogglePreview : null}
         analyticsContext={ANALYTICS_CONTEXT}
       />
     </EventSandbox>
diff --git a/frontend/src/metabase/collections/components/PinnedItemOverview/PinnedItemOverview.tsx b/frontend/src/metabase/collections/components/PinnedItemOverview/PinnedItemOverview.tsx
index 3a00aa81667..f2e4d48960d 100644
--- a/frontend/src/metabase/collections/components/PinnedItemOverview/PinnedItemOverview.tsx
+++ b/frontend/src/metabase/collections/components/PinnedItemOverview/PinnedItemOverview.tsx
@@ -11,7 +11,7 @@ import PinnedItemSortDropTarget from "metabase/collections/components/PinnedItem
 import {
   Item,
   isRootCollection,
-  isPreviewable,
+  isPreviewShown,
 } from "metabase/collections/utils";
 import PinDropZone from "metabase/collections/components/PinDropZone";
 import ItemDragSource from "metabase/containers/dnd/ItemDragSource";
@@ -50,7 +50,7 @@ function PinnedItemOverview({
     dashboard: dashboardItems = [],
     dataset: dataModelItems = [],
   } = _.groupBy(sortedItems, "model");
-  const cardGroups = _.partition(cardItems, isPreviewable);
+  const cardGroups = _.partition(cardItems, isPreviewShown);
 
   return items.length === 0 ? (
     <Container>
diff --git a/frontend/src/metabase/collections/components/PinnedQuestionCard/PinnedQuestionCard.tsx b/frontend/src/metabase/collections/components/PinnedQuestionCard/PinnedQuestionCard.tsx
index 5035af01e3a..339b05aecac 100644
--- a/frontend/src/metabase/collections/components/PinnedQuestionCard/PinnedQuestionCard.tsx
+++ b/frontend/src/metabase/collections/components/PinnedQuestionCard/PinnedQuestionCard.tsx
@@ -2,7 +2,7 @@ import React from "react";
 import { t } from "ttag";
 import {
   hasRequiredParameters,
-  isPreviewable,
+  isPreviewShown,
   Item,
 } from "metabase/collections/utils";
 import Visualization from "metabase/visualizations/components/Visualization";
@@ -37,7 +37,7 @@ const PinnedQuestionCard = ({
   onCreateBookmark,
   onDeleteBookmark,
 }: PinnedQuestionCardProps): JSX.Element => {
-  const isPreview = isPreviewable(item);
+  const isPreview = isPreviewShown(item);
 
   return (
     <CardRoot to={item.getUrl()} isPreview={isPreview}>
diff --git a/frontend/src/metabase/collections/utils.ts b/frontend/src/metabase/collections/utils.ts
index 714379f251a..1207debd685 100644
--- a/frontend/src/metabase/collections/utils.ts
+++ b/frontend/src/metabase/collections/utils.ts
@@ -1,7 +1,5 @@
 import { t } from "ttag";
-import _ from "underscore";
-
-import { Collection, CollectionId } from "metabase-types/api";
+import { Collection } from "metabase-types/api";
 
 export type Item = {
   id: number;
@@ -82,7 +80,7 @@ export function isItemPinned(item: Item) {
   return item.collection_position != null;
 }
 
-export function isPreviewable(item: Item) {
+export function isPreviewShown(item: Item) {
   return isPreviewEnabled(item) && hasRequiredParameters(item);
 }
 
diff --git a/frontend/src/metabase/components/EntityItem.jsx b/frontend/src/metabase/components/EntityItem.jsx
index 8aa232733d5..23cb676342e 100644
--- a/frontend/src/metabase/components/EntityItem.jsx
+++ b/frontend/src/metabase/components/EntityItem.jsx
@@ -85,7 +85,8 @@ function EntityItemName({ name, variant }) {
 function EntityItemMenu({
   item,
   isBookmarked,
-  isPreviewEnabled,
+  isPreviewShown,
+  isPreviewAvailable,
   onPin,
   onMove,
   onCopy,
@@ -108,12 +109,13 @@ function EntityItemMenu({
           event: `${analyticsContext};Entity Item;Pin Item;${item.model}`,
         },
         onTogglePreview && {
-          title: isPreviewEnabled
+          title: isPreviewShown
             ? t`Don’t show visualization`
             : t`Show visualization`,
-          icon: isPreviewEnabled ? "eye_crossed_out" : "eye",
+          icon: isPreviewShown ? "eye_crossed_out" : "eye",
           action: onTogglePreview,
           event: `${analyticsContext};Entity Item;Preview Item;${item.model}`,
+          disabled: !isPreviewAvailable,
         },
         onMove && {
           title: t`Move`,
@@ -144,7 +146,8 @@ function EntityItemMenu({
       item.model,
       isPinned,
       isBookmarked,
-      isPreviewEnabled,
+      isPreviewShown,
+      isPreviewAvailable,
       showPinnedAction,
       onPin,
       onMove,
diff --git a/frontend/src/metabase/components/EntityMenu.jsx b/frontend/src/metabase/components/EntityMenu.jsx
index c1cfd4fbac6..bbcc8092a71 100644
--- a/frontend/src/metabase/components/EntityMenu.jsx
+++ b/frontend/src/metabase/components/EntityMenu.jsx
@@ -140,6 +140,7 @@ class EntityMenu extends Component {
                                   }
                                   event={item.event && item.event}
                                   link={item.link}
+                                  disabled={item.disabled}
                                   onClose={() => {
                                     this.toggleMenu();
                                     item?.onClose?.();
diff --git a/frontend/src/metabase/components/EntityMenuItem.jsx b/frontend/src/metabase/components/EntityMenuItem.jsx
index d6836e11bbf..fcbef50c70b 100644
--- a/frontend/src/metabase/components/EntityMenuItem.jsx
+++ b/frontend/src/metabase/components/EntityMenuItem.jsx
@@ -5,11 +5,19 @@ import { Link } from "react-router";
 import Icon from "metabase/components/Icon";
 import { Item, StyledExternalLink } from "./EntityMenuItem.styled";
 
-const LinkMenuItem = ({ children, link, onClose, event, externalLink }) =>
+const LinkMenuItem = ({
+  children,
+  link,
+  onClose,
+  event,
+  externalLink,
+  disabled,
+}) =>
   externalLink ? (
     <StyledExternalLink
       href={link}
       target="_blank"
+      disabled={disabled}
       onClick={onClose}
       data-metabase-event={event}
     >
@@ -18,6 +26,7 @@ const LinkMenuItem = ({ children, link, onClose, event, externalLink }) =>
   ) : (
     <Link
       to={link}
+      disabled={disabled}
       onClick={onClose}
       data-metabase-event={event}
       className="block"
@@ -26,8 +35,8 @@ const LinkMenuItem = ({ children, link, onClose, event, externalLink }) =>
     </Link>
   );
 
-const ActionMenuItem = ({ children, action, event }) => (
-  <div onClick={action} data-metabase-event={event}>
+const ActionMenuItem = ({ children, action, event, disabled }) => (
+  <div onClick={!disabled ? action : undefined} data-metabase-event={event}>
     {children}
   </div>
 );
@@ -40,6 +49,7 @@ const EntityMenuItem = ({
   onClose,
   event,
   externalLink,
+  disabled,
 }) => {
   if (link && action) {
     console.warn(
@@ -49,7 +59,7 @@ const EntityMenuItem = ({
   }
 
   const content = (
-    <Item>
+    <Item disabled={disabled}>
       {icon && <Icon name={icon} mr={1} />}
       <span className="text-bold">{title}</span>
     </Item>
@@ -60,6 +70,7 @@ const EntityMenuItem = ({
       <LinkMenuItem
         link={link}
         externalLink={externalLink}
+        disabled={disabled}
         onClose={onClose}
         event={event}
       >
@@ -69,7 +80,7 @@ const EntityMenuItem = ({
   }
   if (action) {
     return (
-      <ActionMenuItem action={action} event={event}>
+      <ActionMenuItem action={action} event={event} disabled={disabled}>
         {content}
       </ActionMenuItem>
     );
diff --git a/frontend/src/metabase/components/EntityMenuItem.styled.jsx b/frontend/src/metabase/components/EntityMenuItem.styled.jsx
index 33ca602e79a..040074816ce 100644
--- a/frontend/src/metabase/components/EntityMenuItem.styled.jsx
+++ b/frontend/src/metabase/components/EntityMenuItem.styled.jsx
@@ -5,21 +5,21 @@ import { color } from "metabase/lib/colors";
 
 export const Item = styled.div`
   display: flex;
-align-items: center;
-  cursor: pointer;
-  color: ${color("text-medium")};
+  align-items: center;
+  cursor: ${props => (props.disabled ? "not-allowed" : "pointer")};
+  color: ${props => color(props.disabled ? "text-light" : "text-medium")};
   padding: 0.85em 1.45em;
   text-decoration: none;
   transition: all 300ms linear;
   :hover {
-    color: ${color("brand")};
+    color: ${props => !props.disabled && color("brand")};
   }
   > .Icon {
     color: ${color("text-light")};
     margin-right: 0.65em;
   }
   :hover > .Icon {
-    color: ${color("brand")};
+    color: ${props => !props.disabled && color("brand")};
     transition: all 300ms linear;
   },
   /* icon specific tweaks
-- 
GitLab