From 1d10a19abc76e576994cb64dde3b7bda1949e237 Mon Sep 17 00:00:00 2001 From: Emily Xiong Date: Tue, 1 Oct 2024 15:00:01 -0700 Subject: [PATCH] feat(graph): show owners in pdv (#28211) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Current Behavior ## Expected Behavior Screenshot 2024-09-30 at 8 54 38 PM Screenshot 2024-09-30 at 8 53 49 PM Screenshot 2024-09-30 at 8 52 46 PM ## Related Issue(s) Fixes # --- .../lib/owners-list/owners-list.stories.tsx | 42 ++++++++++ .../src/lib/owners-list/owners-list.tsx | 80 +++++++++++++++++++ .../lib/project-details/project-details.tsx | 8 ++ .../src/config/workspace-json-project-json.ts | 14 ++++ 4 files changed, 144 insertions(+) create mode 100644 graph/ui-project-details/src/lib/owners-list/owners-list.stories.tsx create mode 100644 graph/ui-project-details/src/lib/owners-list/owners-list.tsx diff --git a/graph/ui-project-details/src/lib/owners-list/owners-list.stories.tsx b/graph/ui-project-details/src/lib/owners-list/owners-list.stories.tsx new file mode 100644 index 0000000000..5676694b86 --- /dev/null +++ b/graph/ui-project-details/src/lib/owners-list/owners-list.stories.tsx @@ -0,0 +1,42 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { OwnersList } from './owners-list'; + +const meta: Meta = { + component: OwnersList, + title: 'OwnersList', +}; +export default meta; + +type Story = StoryObj; + +export const FewOwners: Story = { + args: { + owners: ['owner1', 'owner2', 'owner3'], + }, +}; +export const ManyOwners: Story = { + args: { + owners: [ + 'owner1', + 'owner2', + 'owner3', + 'owner4', + 'owner5', + 'owner6', + 'owner7', + 'owner8', + 'owner9', + 'owner10', + 'owner11', + 'owner12', + 'owner13', + 'owner14', + 'owner15', + 'owner16', + 'owner17', + 'owner18', + 'owner19', + 'owner20', + ], + }, +}; diff --git a/graph/ui-project-details/src/lib/owners-list/owners-list.tsx b/graph/ui-project-details/src/lib/owners-list/owners-list.tsx new file mode 100644 index 0000000000..ede0bc8cf2 --- /dev/null +++ b/graph/ui-project-details/src/lib/owners-list/owners-list.tsx @@ -0,0 +1,80 @@ +import { useState, useRef, useEffect } from 'react'; +import { Pill } from '../pill'; +import { twMerge } from 'tailwind-merge'; + +interface OwnersListProps { + owners: string[]; + className?: string; +} + +export function OwnersList({ owners, className }: OwnersListProps) { + const [isExpanded, _setIsExpanded] = useState(false); + const [isOverflowing, setIsOverflowing] = useState(false); + const ownersContainerRef = useRef(null); + + const checkOverflow = () => { + requestAnimationFrame(() => { + if (ownersContainerRef.current) { + setIsOverflowing( + ownersContainerRef.current.scrollWidth > + ownersContainerRef.current.offsetWidth + ); + } + }); + }; + + const setExpanded = (value: boolean) => { + _setIsExpanded(value); + checkOverflow(); + }; + + useEffect(() => { + checkOverflow(); + + window.addEventListener('resize', checkOverflow); + return () => window.removeEventListener('resize', checkOverflow); + }, [ownersContainerRef]); + + return ( +
+

+ Owners: + + + {owners.map((tag) => ( + + + + ))} + {isExpanded && ( + + )} + + {isOverflowing && !isExpanded && ( + + )} +

+
+ ); +} diff --git a/graph/ui-project-details/src/lib/project-details/project-details.tsx b/graph/ui-project-details/src/lib/project-details/project-details.tsx index 026c984d4c..31fe2165ad 100644 --- a/graph/ui-project-details/src/lib/project-details/project-details.tsx +++ b/graph/ui-project-details/src/lib/project-details/project-details.tsx @@ -8,6 +8,7 @@ import { EyeIcon } from '@heroicons/react/24/outline'; import { PropertyInfoTooltip, Tooltip } from '@nx/graph/ui-tooltips'; import { twMerge } from 'tailwind-merge'; import { TagList } from '../tag-list/tag-list'; +import { OwnersList } from '../owners-list/owners-list'; import { TargetConfigurationGroupList } from '../target-configuration-details-group-list/target-configuration-details-group-list'; import { TooltipTriggerText } from '../target-configuration-details/tooltip-trigger-text'; import { TargetTechnologies } from '../target-technologies/target-technologies'; @@ -105,6 +106,13 @@ export const ProjectDetails = ({ {projectData.metadata?.description}

) : null} + {projectData.metadata?.owners && + Object.keys(projectData.metadata?.owners).length ? ( + + ) : null} {projectData.tags && projectData.tags.length ? ( ) : null} diff --git a/packages/nx/src/config/workspace-json-project-json.ts b/packages/nx/src/config/workspace-json-project-json.ts index f10464a995..2ae7a8ddcd 100644 --- a/packages/nx/src/config/workspace-json-project-json.ts +++ b/packages/nx/src/config/workspace-json-project-json.ts @@ -122,6 +122,20 @@ export interface ProjectMetadata { description?: string; technologies?: string[]; targetGroups?: Record; + owners?: { + [ownerId: string]: { + ownedFiles: { + files: ['*'] | string[]; + fromConfig?: { + filePath: string; + location: { + startLine: number; + endLine: number; + }; + }; + }[]; + }; + }; } export interface TargetMetadata {