From 3840d643606265860a519a1d53c9ab4568e65ec1 Mon Sep 17 00:00:00 2001 From: Nate Kelley Date: Mon, 3 Mar 2025 21:16:41 -0700 Subject: [PATCH] popover lineage breadcrumbs --- .../app/auth/_LoginComponents/PolicyCheck.tsx | 2 - .../PermissionLineageBreadcrumb.stories.tsx | 72 ++++++++++++ .../PermissionLineageBreadcrumb.tsx | 108 ++++++++---------- web/src/components/ui/tooltip/Popover.tsx | 10 +- web/src/components/ui/tooltip/PopoverBase.tsx | 16 +-- 5 files changed, 136 insertions(+), 72 deletions(-) create mode 100644 web/src/components/features/PermissionComponents/PermissionLineageBreadcrumb.stories.tsx diff --git a/web/src/app/auth/_LoginComponents/PolicyCheck.tsx b/web/src/app/auth/_LoginComponents/PolicyCheck.tsx index ced08cdef..b45d7ec03 100644 --- a/web/src/app/auth/_LoginComponents/PolicyCheck.tsx +++ b/web/src/app/auth/_LoginComponents/PolicyCheck.tsx @@ -100,8 +100,6 @@ export const PolicyCheck: React.FC<{ } }, [placement]); - console.log(placement); - const alignMemo: PopoverProps['align'] = useMemo(() => { switch (placement) { case 'top': diff --git a/web/src/components/features/PermissionComponents/PermissionLineageBreadcrumb.stories.tsx b/web/src/components/features/PermissionComponents/PermissionLineageBreadcrumb.stories.tsx new file mode 100644 index 000000000..c75708247 --- /dev/null +++ b/web/src/components/features/PermissionComponents/PermissionLineageBreadcrumb.stories.tsx @@ -0,0 +1,72 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { PermissionLineageBreadcrumb } from './PermissionLineageBreadcrumb'; +import type { DatasetPermissionOverviewUser } from '@/api/asset_interfaces'; + +const meta: Meta = { + title: 'Features/Permissions/PermissionLineageBreadcrumb', + component: PermissionLineageBreadcrumb, + tags: ['autodocs'], + parameters: { + layout: 'centered' + } +}; + +export default meta; +type Story = StoryObj; + +// Sample data for different scenarios +const singleLineage: DatasetPermissionOverviewUser['lineage'] = [ + [ + { type: 'user', name: 'John Doe', id: 'user1' }, + { type: 'datasets', name: 'Sales Dataset', id: 'dataset1' }, + { type: 'permissionGroups', name: 'Analysts', id: 'group1' } + ] +]; + +const multipleLineage: DatasetPermissionOverviewUser['lineage'] = [ + [ + { type: 'user', name: 'John Doe', id: 'user1' }, + { type: 'datasets', name: 'Sales Dataset', id: 'dataset1' } + ], + [ + { type: 'user', name: 'John Doe', id: 'user1' }, + { type: 'datasetGroups', name: 'Marketing Data', id: 'datasetGroup1' } + ] +]; + +const emptyLineage: DatasetPermissionOverviewUser['lineage'] = []; + +export const SingleLineageCanQuery: Story = { + args: { + lineage: singleLineage, + canQuery: true + } +}; + +export const SingleLineageCannotQuery: Story = { + args: { + lineage: singleLineage, + canQuery: false + } +}; + +export const MultipleLineageCanQuery: Story = { + args: { + lineage: multipleLineage, + canQuery: true + } +}; + +export const MultipleLineageCannotQuery: Story = { + args: { + lineage: multipleLineage, + canQuery: false + } +}; + +export const NoLineage: Story = { + args: { + lineage: emptyLineage, + canQuery: false + } +}; diff --git a/web/src/components/features/PermissionComponents/PermissionLineageBreadcrumb.tsx b/web/src/components/features/PermissionComponents/PermissionLineageBreadcrumb.tsx index e4bb9e34b..431cb7015 100644 --- a/web/src/components/features/PermissionComponents/PermissionLineageBreadcrumb.tsx +++ b/web/src/components/features/PermissionComponents/PermissionLineageBreadcrumb.tsx @@ -1,10 +1,12 @@ import type { DatasetPermissionOverviewUser } from '@/api/asset_interfaces'; -import { AppMaterialIcons, AppPopover } from '@/components/ui'; +import { ChevronRight } from '@/components/ui/icons'; +import { Popover } from '@/components/ui/tooltip/Popover'; import { BusterRoutes, createBusterRoute } from '@/routes'; import { useMemoizedFn } from 'ahooks'; -import { createStyles } from 'antd-style'; import Link from 'next/link'; import React, { useMemo } from 'react'; +import { cn } from '@/lib/utils'; +import { cva } from 'class-variance-authority'; export const PermissionLineageBreadcrumb: React.FC<{ lineage: DatasetPermissionOverviewUser['lineage']; @@ -56,10 +58,10 @@ const MultipleLineage: React.FC<{ lineage: DatasetPermissionOverviewUser['lineage']; canQuery: DatasetPermissionOverviewUser['can_query']; }> = ({ lineage, canQuery }) => { - const { styles, cx } = useStyles(); + // const { styles, cx } = useStyles(); const Content = useMemo(() => { return ( -
+
{lineage.map((item, lineageindex) => { const items = item.map((v, index) => { return ; @@ -71,17 +73,10 @@ const MultipleLineage: React.FC<{ ); }, [lineage]); - const onClickPreflight = useMemoizedFn((e: React.MouseEvent) => { - e.stopPropagation(); - e.preventDefault(); - }); - return ( - -
- Multiple access sources -
-
+ +
Multiple access sources
+
); }; @@ -91,39 +86,30 @@ interface LineageItemProps { } const UserLineageItem: React.FC = ({ name, id }) => { - const { styles, cx } = useStyles(); - return
{name}
; + return
{name}
; }; const DatasetLineageItem: React.FC = ({ name, id }) => { - const { styles, cx } = useStyles(); return ( -
{name}
+
{name}
); }; const PermissionGroupLineageItem: React.FC = ({ name, id }) => { - const { styles, cx } = useStyles(); - return
{name}
; + return
{name}
; }; const DatasetGroupLineageItem: React.FC = ({ name, id }) => { - const { styles, cx } = useStyles(); - return
{name}
; + return
{name}
; }; const CanQueryTag: React.FC<{ canQuery: boolean; }> = ({ canQuery }) => { - const { styles, cx } = useStyles(); return ( -
+
{canQuery ? 'Can query' : 'Cannot query'}
); @@ -133,13 +119,12 @@ const LineageBreadcrumb: React.FC<{ items: React.ReactNode[]; canQuery: boolean; }> = ({ items, canQuery }) => { - const { styles, cx } = useStyles(); - const BreadcrumbIcon = ; + const BreadcrumbIcon = ; const allItems = [...items, ]; return ( -
+
{allItems.map((item, index) => { return (
@@ -152,33 +137,36 @@ const LineageBreadcrumb: React.FC<{ ); }; -const useStyles = createStyles(({ token, css }) => ({ - linearContainer: css` - color: ${token.colorTextSecondary}; - `, - linearItem: css` - color: ${token.colorTextSecondary}; - padding: 4px 6px; - border-radius: 4px; - - &.clickable { - cursor: pointer; - &:hover { - color: ${token.colorText}; - background-color: ${token.colorFillSecondary}; - } +const linearItem = cva('text-text-secondary text-base px-1 py-1.5 rounded-sm', { + variants: { + clickable: { + true: 'cursor-pointer hover:text-text hover:bg-item-hover-active', + false: '' } - `, - canQueryTag: css` - border-radius: 4px; - padding: 4px 6px; - `, - canQueryTagSuccess: css` - color: #34a32d; - background-color: #edfff0; - `, - canQueryTagError: css` - color: #ff9e00; - background-color: #fff7ed; - ` -})); + } +}); + +const canQueryTag = cva('rounded-sm px-1 py-1.5 text-base', { + variants: { + status: { + success: 'bg-success-background text-success-foreground', + error: 'bg-danger-background text-danger-foreground' + } + } +}); + +// const useStyles = createStyles(({ token, css }) => ({ + +// canQueryTag: css` +// border-radius: 4px; +// padding: 4px 6px; +// `, +// canQueryTagSuccess: css` +// color: #34a32d; +// background-color: #edfff0; +// `, +// canQueryTagError: css` +// color: #ff9e00; +// background-color: #fff7ed; +// ` +// })); diff --git a/web/src/components/ui/tooltip/Popover.tsx b/web/src/components/ui/tooltip/Popover.tsx index 8d01d02fa..c1161cb68 100644 --- a/web/src/components/ui/tooltip/Popover.tsx +++ b/web/src/components/ui/tooltip/Popover.tsx @@ -1,6 +1,7 @@ import { PopoverRoot as PopoverBase, PopoverContent, + PopoverContentVariant, PopoverTrigger, PopoverTriggerType } from './PopoverBase'; @@ -14,7 +15,8 @@ export interface PopoverProps content: React.ReactNode; className?: string; headerContent?: string | React.ReactNode; - triggerType?: PopoverTriggerType; + trigger?: PopoverTriggerType; + size?: PopoverContentVariant['size']; } export const Popover = React.memo( @@ -25,16 +27,18 @@ export const Popover = React.memo( side, className = '', headerContent, - triggerType = 'click', + trigger = 'click', + size = 'default', ...props }) => { return ( - + {children} }> {content} diff --git a/web/src/components/ui/tooltip/PopoverBase.tsx b/web/src/components/ui/tooltip/PopoverBase.tsx index 4bc69cd9c..24af6edf3 100644 --- a/web/src/components/ui/tooltip/PopoverBase.tsx +++ b/web/src/components/ui/tooltip/PopoverBase.tsx @@ -12,26 +12,26 @@ export type PopoverTriggerType = 'click' | 'hover'; const Popover = PopoverPrimitive.Root; interface PopoverProps extends React.ComponentPropsWithoutRef { - triggerType?: PopoverTriggerType; + trigger?: PopoverTriggerType; } -const PopoverRoot: React.FC = ({ children, triggerType = 'click', ...props }) => { +const PopoverRoot: React.FC = ({ children, trigger = 'click', ...props }) => { const [isOpen, setIsOpen] = React.useState(false); const handleMouseEnter = useMemoizedFn(() => { - if (triggerType === 'hover') { + if (trigger === 'hover') { setIsOpen(true); } }); const handleMouseLeave = useMemoizedFn(() => { - if (triggerType === 'hover') { + if (trigger === 'hover') { setIsOpen(false); } }); const content = - triggerType === 'hover' ? ( + trigger === 'hover' ? (
{children}
@@ -41,7 +41,7 @@ const PopoverRoot: React.FC = ({ children, triggerType = 'click', ); return ( - + {content} ); @@ -64,11 +64,13 @@ const popoverContentVariant = cva('', { } }); +export type PopoverContentVariant = VariantProps; + const PopoverContent = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef & { headerContent?: React.ReactNode; - } & VariantProps + } & PopoverContentVariant >( ( { className, align = 'center', children, sideOffset = 4, headerContent, size, ...props },