diff --git a/graph/client/src/app/external-api-impl.ts b/graph/client/src/app/external-api-impl.ts index 839103bac26c4c..724c9ebf878171 100644 --- a/graph/client/src/app/external-api-impl.ts +++ b/graph/client/src/app/external-api-impl.ts @@ -94,7 +94,7 @@ export class ExternalApiImpl extends ExternalApi { const currentLocation = this.router.state.location; const searchParams = new URLSearchParams(currentLocation.search); - searchParams.set('targetName', targetName); + searchParams.set('expanded', targetName); const newUrl = `${currentLocation.pathname}?${searchParams.toString()}`; this.router.navigate(newUrl); diff --git a/graph/client/src/app/feature-projects/project-list.tsx b/graph/client/src/app/feature-projects/project-list.tsx index f6c1f82f3f7ef2..fed918e0ed075d 100644 --- a/graph/client/src/app/feature-projects/project-list.tsx +++ b/graph/client/src/app/feature-projects/project-list.tsx @@ -6,7 +6,7 @@ import { } from '@heroicons/react/24/outline'; /* eslint-disable @nx/enforce-module-boundaries */ // nx-ignore-next-line -import type { ProjectGraphNode } from '@nx/devkit'; +import type { ProjectGraphProjectNode } from '@nx/devkit'; /* eslint-enable @nx/enforce-module-boundaries */ import { useProjectGraphSelector } from './hooks/use-project-graph-selector'; import { @@ -23,7 +23,7 @@ import { Link, useNavigate } from 'react-router-dom'; import { useRouteConstructor } from '@nx/graph/shared'; interface SidebarProject { - projectGraphNode: ProjectGraphNode; + projectGraphNode: ProjectGraphProjectNode; isSelected: boolean; } @@ -36,7 +36,7 @@ interface TracingInfo { } function groupProjectsByDirectory( - projects: ProjectGraphNode[], + projects: ProjectGraphProjectNode[], selectedProjects: string[], workspaceLayout: { appsDir: string; libsDir: string } ): DirectoryProjectRecord { diff --git a/graph/client/src/app/util.ts b/graph/client/src/app/util.ts index a3d71d40f9dcee..e4e8cf503b129e 100644 --- a/graph/client/src/app/util.ts +++ b/graph/client/src/app/util.ts @@ -1,7 +1,9 @@ /* eslint-disable @nx/enforce-module-boundaries */ // nx-ignore-next-line -import { ProjectGraphDependency, ProjectGraphProjectNode } from '@nx/devkit'; -import { getEnvironmentConfig } from '@nx/graph/shared'; +import type { + ProjectGraphDependency, + ProjectGraphProjectNode, +} from '@nx/devkit'; /* eslint-enable @nx/enforce-module-boundaries */ export function parseParentDirectoriesFromFilePath( diff --git a/graph/project-details/src/lib/project-details-wrapper.state.ts b/graph/project-details/src/lib/project-details-wrapper.state.ts index 8c68e2aaf51124..2ca6c928310574 100644 --- a/graph/project-details/src/lib/project-details-wrapper.state.ts +++ b/graph/project-details/src/lib/project-details-wrapper.state.ts @@ -3,14 +3,11 @@ import { RootState, expandTargetActions, getExpandedTargets, - getSelectedTarget, - selectTargetActions, } from '@nx/graph/state'; const mapStateToProps = (state: RootState) => { return { expandTargets: getExpandedTargets(state), - getSelectedTarget: getSelectedTarget(state), }; }; @@ -19,15 +16,9 @@ const mapDispatchToProps = (dispatch: AppDispatch) => { setExpandTargets(targets: string[]) { dispatch(expandTargetActions.setExpandTargets(targets)); }, - selectTarget(targetGroup: string) { - dispatch(selectTargetActions.selectTarget(targetGroup)); - }, collapseAllTargets() { dispatch(expandTargetActions.collapseAllTargets()); }, - clearTargetGroup() { - dispatch(selectTargetActions.clearSelectedTarget()); - }, }; }; diff --git a/graph/project-details/src/lib/project-details-wrapper.tsx b/graph/project-details/src/lib/project-details-wrapper.tsx index 3bf227c7e06201..0e98e374704901 100644 --- a/graph/project-details/src/lib/project-details-wrapper.tsx +++ b/graph/project-details/src/lib/project-details-wrapper.tsx @@ -8,11 +8,7 @@ import { useEnvironmentConfig, useRouteConstructor, } from '@nx/graph/shared'; -import { - ProjectDetails, - defaultSelectTargetGroup, - getTargetGroupForTarget, -} from '@nx/graph/ui-project-details'; +import { ProjectDetails } from '@nx/graph/ui-project-details'; import { useCallback, useEffect } from 'react'; import { mapStateToProps, @@ -32,9 +28,6 @@ export function ProjectDetailsWrapperComponent({ sourceMap, setExpandTargets, expandTargets, - getSelectedTarget, - selectTarget, - clearTargetGroup, collapseAllTargets, }: ProjectDetailsProps) { const environment = useEnvironmentConfig()?.environment; @@ -101,89 +94,54 @@ export function ProjectDetailsWrapperComponent({ const updateSearchParams = ( params: URLSearchParams, - targetGroup: string | null, targetNames: string[] ) => { - if (targetGroup) { - params.set('targetGroup', targetGroup); - } else { - params.delete('targetGroup'); - } if (targetNames.length === 0) { - params.delete('targetName'); + params.delete('expanded'); } else { - params.set('targetName', targetNames.join(',')); + params.set('expanded', targetNames.join(',')); } }; - /* useEffect(() => { + useEffect(() => { if (!project.data.targets) return; - const selectedTargetNameParam = searchParams.get('targetName'); - if ( - selectedTargetNameParam && - selectedTarget !== selectedTargetNameParam - ) { - selectTarget(selectedTargetNameParam); - } - - const expandedTargetsParams = - searchParams.get('targetName')?.split(',') || []; - if (expandedTargetsParams.length > 0) { + const expandedTargetsParams = searchParams.get('expanded')?.split(','); + if (expandedTargetsParams && expandedTargetsParams.length > 0) { setExpandTargets(expandedTargetsParams); } - const targetName = searchParams.get('targetName'); - if (targetName) { - const targetGroup = getTargetGroupForTarget(targetName, project); - selectTarget(targetGroup); - setExpandTargets([targetName]); - } - return () => { - clearTargetGroup(); collapseAllTargets(); - searchParams.delete('targetGroup'); - searchParams.delete('targetName'); + searchParams.delete('expanded'); setSearchParams(searchParams, { replace: true }); }; }, []); // only run on mount - useEffect(() => { if (!project.data.targets) return; - const selectedTargetGroupParams = searchParams.get('targetGroup'); const expandedTargetsParams = - searchParams.get('targetName')?.split(',') || []; + searchParams.get('expanded')?.split(',') || []; - if ( - selectedTargetGroup === selectedTargetGroupParams && - expandedTargetsParams.join(',') === expandTargets.join(',') - ) { + if (expandedTargetsParams.join(',') === expandTargets.join(',')) { return; } setSearchParams( (currentSearchParams) => { - updateSearchParams( - currentSearchParams, - selectedTargetGroup, - expandTargets - ); + updateSearchParams(currentSearchParams, expandTargets); return currentSearchParams; }, { replace: true, preventScrollReset: true } ); }, [ expandTargets, - selectedTargetGroup, project.data.targets, setExpandTargets, searchParams, setSearchParams, ]); - */ return ( ({ [EXPAND_TARGETS_KEY]: expandTargetReducer, - [SELECT_TARGET_KEY]: selectTargetReducer as any, }); diff --git a/graph/state/src/lib/select-target/select-target.slice.ts b/graph/state/src/lib/select-target/select-target.slice.ts deleted file mode 100644 index 58daaeb5e1ef79..00000000000000 --- a/graph/state/src/lib/select-target/select-target.slice.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { PayloadAction, createSlice } from '@reduxjs/toolkit'; - -export const SELECT_TARGET_KEY = 'selectTarget'; - -export const initialSelectTarget: string = ''; - -export const selectTargetSlice = createSlice({ - name: SELECT_TARGET_KEY, - initialState: initialSelectTarget, - reducers: { - selectTarget: (_: string, action: PayloadAction): string => { - return action.payload; - }, - clearSelectedTarget: (): string => { - return ''; - }, - }, -}); - -/* - * Export reducer for store configuration. - */ -export const selectTargetReducer = selectTargetSlice.reducer; - -export const selectTargetActions = selectTargetSlice.actions; - -export const getSelectedTarget = ( - rootState: ROOT -): string => rootState[SELECT_TARGET_KEY]; diff --git a/graph/ui-icons/src/lib/framework-icons.stories.tsx b/graph/ui-icons/src/lib/framework-icons.stories.tsx index b02eeb30528d55..e57eaf595c9b2f 100644 --- a/graph/ui-icons/src/lib/framework-icons.stories.tsx +++ b/graph/ui-icons/src/lib/framework-icons.stories.tsx @@ -19,10 +19,6 @@ const meta: Meta = { export default meta; type Story = StoryObj; -export const Primary = { - args: {}, -}; - -export const Heading: Story = { +export const Primary: Story = { args: {}, }; diff --git a/graph/ui-icons/src/lib/framework-icons.tsx b/graph/ui-icons/src/lib/framework-icons.tsx index 112c0fdb8befbc..697c66f0dccda2 100644 --- a/graph/ui-icons/src/lib/framework-icons.tsx +++ b/graph/ui-icons/src/lib/framework-icons.tsx @@ -68,6 +68,7 @@ export const frameworkIcons: Record< reactMono: { image: ( @@ -293,8 +301,8 @@ export const frameworkIcons: Record< nestjs: { image: ( @@ -420,8 +428,8 @@ export const frameworkIcons: Record< storybook: { image: ( + Solid @@ -1675,6 +1707,7 @@ export const frameworkIcons: Record< mfe: { image: ( - {frameworkIcons[technology as Framework].image} + {image ?? technology[0]} ); } diff --git a/graph/ui-project-details/src/lib/project-details/project-details.stories.tsx b/graph/ui-project-details/src/lib/project-details/project-details.stories.tsx index 9fffd027df1058..fb98d5e58d2db3 100644 --- a/graph/ui-project-details/src/lib/project-details/project-details.stories.tsx +++ b/graph/ui-project-details/src/lib/project-details/project-details.stories.tsx @@ -619,7 +619,7 @@ export const Gradle = { }, }; -export const CartE2e = { +export const Cart = { args: { project: { name: 'cart-e2e', 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 62da3e4575e2f3..0e47fff8f9eac0 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 @@ -9,8 +9,8 @@ import { PropertyInfoTooltip, Tooltip } from '@nx/graph/ui-tooltips'; import { TooltipTriggerText } from '../target-configuration-details/tooltip-trigger-text'; import { twMerge } from 'tailwind-merge'; import { Pill } from '../pill'; -import { TargetGroups } from '../target-groups/target-groups'; import { TargetConfigurationDetailsList } from '../target-configuration-details-list/target-configuration-details-list'; +import { TargetTechnologies } from '../target-technologies/target-technologies'; export interface ProjectDetailsProps { project: ProjectGraphProjectNode; @@ -40,6 +40,17 @@ export const ProjectDetails = ({ projectData.projectType?.charAt(0)?.toUpperCase() + projectData.projectType?.slice(1); + const technologies = [ + ...new Set( + [ + ...(projectData.metadata?.technologies ?? []), + ...Object.values(projectData.targets ?? {}) + .map((target) => target?.metadata?.technologies) + .flat(), + ].filter(Boolean) + ), + ] as string[]; + return ( <>
-

- {project.name} +
+

+ {project.name} +

+ +
{onViewInProjectGraph ? (

+
{projectData.tags && projectData.tags.length ? (

diff --git a/graph/ui-project-details/src/lib/source-info/source-info.tsx b/graph/ui-project-details/src/lib/source-info/source-info.tsx index 893ad2f7b07343..5b185dae3f8dab 100644 --- a/graph/ui-project-details/src/lib/source-info/source-info.tsx +++ b/graph/ui-project-details/src/lib/source-info/source-info.tsx @@ -11,7 +11,7 @@ export function SourceInfo(props: { // Every other property within in the target has the form `target.${targetName}.${propertyName} const isTarget = props.propertyKey.split('.').length === 2; return ( - + + ref: React.Ref ) => { return ( -

-
- {targetGroupName}{' '} - +
+ -
-
- {children} -
+
+ {children} +
+
); } diff --git a/graph/ui-project-details/src/lib/target-configuration-details-group-header/target-configuration-details-group-header.stories.tsx b/graph/ui-project-details/src/lib/target-configuration-details-group-header/target-configuration-details-group-header.stories.tsx new file mode 100644 index 00000000000000..b9051371c324d2 --- /dev/null +++ b/graph/ui-project-details/src/lib/target-configuration-details-group-header/target-configuration-details-group-header.stories.tsx @@ -0,0 +1,17 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { TargetConfigurationGroupHeader } from './target-configuration-details-group-header'; + +const meta: Meta = { + component: TargetConfigurationGroupHeader, + title: 'TargetConfigurationGroupHeader', +}; +export default meta; + +type Story = StoryObj; + +export const Simple: Story = { + args: { + targetGroupName: 'Target Group Name', + targetsNumber: 5, + }, +}; diff --git a/graph/ui-project-details/src/lib/target-configuration-details-group-header/target-configuration-details-group-header.tsx b/graph/ui-project-details/src/lib/target-configuration-details-group-header/target-configuration-details-group-header.tsx new file mode 100644 index 00000000000000..06b7a72c086f7a --- /dev/null +++ b/graph/ui-project-details/src/lib/target-configuration-details-group-header/target-configuration-details-group-header.tsx @@ -0,0 +1,25 @@ +import { Pill } from '../pill'; + +export interface TargetConfigurationGroupHeaderProps { + targetGroupName: string; + targetsNumber: number; + className?: string; +} + +export const TargetConfigurationGroupHeader = ({ + targetGroupName, + targetsNumber, + className = '', +}: TargetConfigurationGroupHeaderProps) => { + return ( +
+ {targetGroupName}{' '} + +
+ ); +}; diff --git a/graph/ui-project-details/src/lib/target-configuration-details-group-list/target-configuration-details-group-list.stories.tsx b/graph/ui-project-details/src/lib/target-configuration-details-group-list/target-configuration-details-group-list.stories.tsx new file mode 100644 index 00000000000000..05c5a40af31d1d --- /dev/null +++ b/graph/ui-project-details/src/lib/target-configuration-details-group-list/target-configuration-details-group-list.stories.tsx @@ -0,0 +1,94 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { + TargetConfigurationGroupList, + TargetConfigurationGroupListProps, +} from './target-configuration-details-group-list'; +import { StoreDecorator } from '@nx/graph/state'; + +const meta: Meta = { + component: TargetConfigurationGroupList, + title: 'TargetConfigurationGroupList', + decorators: [StoreDecorator], +}; +export default meta; + +type Story = StoryObj; + +export const OneTarget: Story = { + args: { + project: { + name: 'react', + type: 'lib', + data: { + root: 'libs/react', + targets: { + build: { + executor: 'nx', + options: {}, + configurations: { + production: { + executor: 'nx', + options: {}, + }, + }, + }, + lint: { + executor: 'nx', + options: {}, + }, + }, + }, + }, + sourceMap: { + react: ['react'], + }, + variant: 'default', + onRunTarget: () => {}, + onViewInTaskGraph: () => {}, + selectedTargetGroup: 'build', + setExpandTargets: () => {}, + collapseAllTargets: () => {}, + } as TargetConfigurationGroupListProps, +}; + +export const TwoTargets: Story = { + args: { + project: { + name: 'react', + type: 'lib', + data: { + root: 'libs/react', + targets: { + build1: { + executor: 'nx', + options: {}, + configurations: { + production: { + executor: 'nx', + options: {}, + }, + }, + }, + build2: { + executor: 'nx', + options: {}, + }, + }, + metadata: { + targetGroups: { + build: ['build1', 'build2'], + }, + }, + }, + }, + sourceMap: { + react: ['react'], + }, + variant: 'default', + onRunTarget: () => {}, + onViewInTaskGraph: () => {}, + selectedTargetGroup: 'build', + setExpandTargets: () => {}, + collapseAllTargets: () => {}, + } as TargetConfigurationGroupListProps, +}; diff --git a/graph/ui-project-details/src/lib/target-configuration-details-group-list/target-configuration-details-group-list.tsx b/graph/ui-project-details/src/lib/target-configuration-details-group-list/target-configuration-details-group-list.tsx index 5fc9ea5288f83e..a561781739b3c2 100644 --- a/graph/ui-project-details/src/lib/target-configuration-details-group-list/target-configuration-details-group-list.tsx +++ b/graph/ui-project-details/src/lib/target-configuration-details-group-list/target-configuration-details-group-list.tsx @@ -1,12 +1,13 @@ /* eslint-disable @nx/enforce-module-boundaries */ // nx-ignore-next-line import type { ProjectGraphProjectNode } from '@nx/devkit'; -import { TargetConfigurationDetailsListItem } from '../target-configuration-details-list-item/target-configuration-details-list-item'; -import { TargetConfigurationGroupContainer } from '../target-configuration-details-group-container/target-configuration-details-group-container'; import { RefObject, createRef, useEffect, useRef, useState } from 'react'; -import { TargetConfigurationDetailsHeader } from '../target-configuration-details-header/target-configuration-details-header'; import { Transition } from '@headlessui/react'; -import { Pill } from '../pill'; + +import { TargetConfigurationDetailsListItem } from '../target-configuration-details-list-item/target-configuration-details-list-item'; +import { TargetConfigurationGroupContainer } from '../target-configuration-details-group-container/target-configuration-details-group-container'; +import { TargetConfigurationGroupHeader } from '../target-configuration-details-group-header/target-configuration-details-group-header'; +import { groupTargets } from '../utils/group-targets'; export interface TargetConfigurationGroupListProps { project: ProjectGraphProjectNode; @@ -18,7 +19,6 @@ export interface TargetConfigurationGroupListProps { targetName: string; }) => void; className?: string; - isCompact?: boolean; } export function TargetConfigurationGroupList({ @@ -27,22 +27,18 @@ export function TargetConfigurationGroupList({ sourceMap, onRunTarget, onViewInTaskGraph, - className, - isCompact, + className = '', }: TargetConfigurationGroupListProps) { const [stickyHeaderContent, setStickHeaderContent] = useState(''); - const [stickyTargetName, setStickyTargetName] = useState(''); + const targetsGroup = groupTargets(project); const targetGroupRefs = useRef( - Object.keys(project.data.metadata?.targetGroups ?? {}).reduce( - (acc, targetGroupName) => { - acc[targetGroupName] = createRef(); - return acc; - }, - {} as Record> - ) + Object.keys(targetsGroup.groups).reduce((acc, targetGroupName) => { + acc[targetGroupName] = createRef(); + return acc; + }, {} as Record>) ); const targetNameRefs = useRef( - Object.keys(project.data.targets ?? {}).reduce((acc, targetName) => { + targetsGroup.targets.reduce((acc, targetName) => { acc[targetName] = createRef(); return acc; }, {} as Record>) @@ -56,7 +52,7 @@ export function TargetConfigurationGroupList({ }, []); const isSticky = () => { - const scrollTop = window.scrollY + 20; + const scrollTop = window.scrollY + 30; // 30px for the header const foundTargetGroup: string | undefined = Object.keys( targetGroupRefs.current ).find((targetGroupName) => { @@ -77,28 +73,6 @@ export function TargetConfigurationGroupList({ } else { setStickHeaderContent(''); } - - if (!targetNameRefs.current) return; - - const foundTargetName: string | undefined = Object.keys( - targetNameRefs.current - ).find((targetName) => { - const target = targetNameRefs.current[targetName]; - if ( - target && - target.current && - scrollTop >= target.current.offsetTop && - scrollTop < target.current.offsetTop + target.current.offsetHeight - ) { - return true; - } - return false; - }); - if (foundTargetName) { - setStickyTargetName(foundTargetName); - } else { - setStickyTargetName(''); - } }; return ( @@ -112,70 +86,60 @@ export function TargetConfigurationGroupList({ leaveFrom="opacity-100" leaveTo="opacity-0" > -
-
-
- {stickyHeaderContent}{' '} - -
+
+
+
-
    - {Object.entries(project.data.metadata?.targetGroups ?? {}).map( - ([targetGroupName, targets], index) => { - return ( - - {targets.map((targetName) => ( - - ))} - - ); - } - )} - {Object.keys(project.data.targets ?? {}).map((targetName) => { - if ( - !project.data.metadata?.targetGroups || - !Object.values(project.data.metadata?.targetGroups ?? {}) - .flat() - .includes(targetName) - ) { - return ( - - ); - } - return null; + {Object.entries(targetsGroup.groups).map(([targetGroupName, targets]) => { + return ( + +
      + {targets.map((targetName) => ( + + ))} +
    +
    + ); + })} +
      + {targetsGroup.targets.map((targetName) => { + return ( + + ); })}
    diff --git a/graph/ui-project-details/src/lib/target-configuration-details-header/target-configuration-details-header.tsx b/graph/ui-project-details/src/lib/target-configuration-details-header/target-configuration-details-header.tsx index 4379c253997d6b..53f47960d21fda 100644 --- a/graph/ui-project-details/src/lib/target-configuration-details-header/target-configuration-details-header.tsx +++ b/graph/ui-project-details/src/lib/target-configuration-details-header/target-configuration-details-header.tsx @@ -70,21 +70,21 @@ export const TargetConfigurationDetailsHeader = ({ onClick={collapsable ? toggleCollapse : undefined} >
    -
    +
    {collapsable && (isCollasped ? ( ) : ( ))} +

    {targetName}

    -

    {targetName}

    {isCollasped && targetConfiguration?.executor !== '@nx/js:release-publish' && ( -

    +

    {singleCommand ? singleCommand : targetConfiguration.executor}

    )} @@ -148,16 +148,14 @@ export const TargetConfigurationDetailsHeader = ({
    {!isCollasped && ( -
    - - - +
    + {targetName !== 'nx-release-publish' && ( -
    +
    nx run {projectName}:{targetName} diff --git a/graph/ui-project-details/src/lib/target-configuration-details-list-item/target-configuration-details-list-item.stories.tsx b/graph/ui-project-details/src/lib/target-configuration-details-list-item/target-configuration-details-list-item.stories.tsx index 4bdf0ab800b1e1..4df1ce5bfec538 100644 --- a/graph/ui-project-details/src/lib/target-configuration-details-list-item/target-configuration-details-list-item.stories.tsx +++ b/graph/ui-project-details/src/lib/target-configuration-details-list-item/target-configuration-details-list-item.stories.tsx @@ -3,10 +3,12 @@ import { TargetConfigurationDetailsListItem, TargetConfigurationDetailsListItemProps, } from './target-configuration-details-list-item'; +import { StoreDecorator } from '@nx/graph/state'; const meta: Meta = { component: TargetConfigurationDetailsListItem, title: 'TargetConfigurationDetailsListItem', + decorators: [StoreDecorator], }; export default meta; diff --git a/graph/ui-project-details/src/lib/target-configuration-details-list/target-configuration-details-list.state.ts b/graph/ui-project-details/src/lib/target-configuration-details-list/target-configuration-details-list.state.ts index 7be9c071e4804a..2e4ed592be2c02 100644 --- a/graph/ui-project-details/src/lib/target-configuration-details-list/target-configuration-details-list.state.ts +++ b/graph/ui-project-details/src/lib/target-configuration-details-list/target-configuration-details-list.state.ts @@ -1,16 +1,9 @@ /* eslint-disable @nx/enforce-module-boundaries */ // nx-ignore-next-line -import { - AppDispatch, - RootState, - getSelectedTarget, - expandTargetActions, -} from '@nx/graph/state'; +import { AppDispatch, RootState, expandTargetActions } from '@nx/graph/state'; const mapStateToProps = (state: RootState) => { - return { - selectedTarget: getSelectedTarget(state), - }; + return {}; }; const mapDispatchToProps = (dispatch: AppDispatch) => { diff --git a/graph/ui-project-details/src/lib/target-configuration-details-list/target-configuration-details-list.tsx b/graph/ui-project-details/src/lib/target-configuration-details-list/target-configuration-details-list.tsx index 8d0972db0fadcb..456bb3218a9e1b 100644 --- a/graph/ui-project-details/src/lib/target-configuration-details-list/target-configuration-details-list.tsx +++ b/graph/ui-project-details/src/lib/target-configuration-details-list/target-configuration-details-list.tsx @@ -2,16 +2,12 @@ import { connect } from 'react-redux'; /* eslint-disable @nx/enforce-module-boundaries */ // nx-ignore-next-line import type { ProjectGraphProjectNode } from '@nx/devkit'; -import TargetConfigurationDetails from '../target-configuration-details/target-configuration-details'; import { mapDispatchToProps, mapDispatchToPropsType, mapStateToProps, mapStateToPropsType, } from './target-configuration-details-list.state'; -import { useEffect } from 'react'; -import { groupTargets } from '../utils/group-targets'; -import { TargetConfigurationDetailsListItem } from '../target-configuration-details-list-item/target-configuration-details-list-item'; import { TargetConfigurationGroupList } from '../target-configuration-details-group-list/target-configuration-details-group-list'; export type TargetConfigurationDetailsListProps = mapStateToPropsType & @@ -34,22 +30,7 @@ export function TargetConfigurationDetailsListComponent({ onRunTarget, onViewInTaskGraph, className, - selectedTarget, }: TargetConfigurationDetailsListProps) { - if (selectedTarget) { - return ( - - ); - } - return ( {sourceInfo && ( - + {sourceInfo && ( - + - + {sourceInfo && ( + + + {data} {children} @@ -20,7 +20,7 @@ export function TargetConfigurationProperty({ return (
      {data.map((item, index) => ( -
    • +
    • {String(item)} {children}
    • @@ -31,7 +31,7 @@ export function TargetConfigurationProperty({ return (
        {Object.entries(data).map(([key, value], index) => ( -
      • +
      • {key}: {String(value)} {children}
      • diff --git a/graph/ui-project-details/src/lib/target-group/target-group.stories.tsx b/graph/ui-project-details/src/lib/target-group/target-group.stories.tsx deleted file mode 100644 index 95e03ed56af0f6..00000000000000 --- a/graph/ui-project-details/src/lib/target-group/target-group.stories.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; -import { TargetGroup, TargetGroupProps } from './target-group'; - -const meta: Meta = { - component: TargetGroup, - title: 'TargetGroup', -}; -export default meta; - -type Story = StoryObj; - -export const Simple: Story = { - args: { - name: 'react', - selected: false, - isCompact: false, - onClick: () => {}, - }, -}; - -export const Compact: Story = { - args: { - name: 'react', - selected: false, - isCompact: true, - onClick: () => {}, - } as TargetGroupProps, -}; diff --git a/graph/ui-project-details/src/lib/target-group/target-group.tsx b/graph/ui-project-details/src/lib/target-group/target-group.tsx deleted file mode 100644 index c61531778694cc..00000000000000 --- a/graph/ui-project-details/src/lib/target-group/target-group.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { ChevronRightIcon } from '@heroicons/react/24/outline'; -import { twMerge } from 'tailwind-merge'; -import { TargetTechnologies } from '../target-technologies/target-technologies'; - -export interface TargetGroupProps { - name: string; - selected: boolean; - isCompact: boolean; - onClick: (name: string) => void; - technologies?: string[]; -} - -export function TargetGroup({ - selected, - name, - onClick, - isCompact, - technologies, -}: TargetGroupProps) { - return ( -
      • onClick(name)} - > -
        -

        - - {name} -

        - -
        -
      • - ); -} diff --git a/graph/ui-project-details/src/lib/target-groups/target-groups.state.ts b/graph/ui-project-details/src/lib/target-groups/target-groups.state.ts deleted file mode 100644 index 2fe31327f92cfc..00000000000000 --- a/graph/ui-project-details/src/lib/target-groups/target-groups.state.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { - AppDispatch, - RootState, - selectTargetActions, - getSelectedTarget, -} from '@nx/graph/state'; - -const mapStateToProps = (state: RootState) => { - return { - selectedTargetGroup: getSelectedTarget(state), - }; -}; - -const mapDispatchToProps = (dispatch: AppDispatch) => { - return { - selectTargetGroup(targetGroup: string) { - dispatch(selectTargetActions.selectTarget(targetGroup)); - }, - }; -}; - -type mapStateToPropsType = ReturnType; -type mapDispatchToPropsType = ReturnType; - -export { - mapStateToProps, - mapDispatchToProps, - mapStateToPropsType, - mapDispatchToPropsType, -}; diff --git a/graph/ui-project-details/src/lib/target-groups/target-groups.stories.tsx b/graph/ui-project-details/src/lib/target-groups/target-groups.stories.tsx deleted file mode 100644 index 7d3d45ee62e35b..00000000000000 --- a/graph/ui-project-details/src/lib/target-groups/target-groups.stories.tsx +++ /dev/null @@ -1,142 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; -import { TargetGroupsComponent, TargetGroupsProps } from './target-groups'; - -const meta: Meta = { - component: TargetGroupsComponent, - title: 'TargetGroupsComponent', -}; -export default meta; - -type Story = StoryObj; - -export const BuildBaseSelected: Story = { - args: { - project: { - name: 'jest', - type: 'lib', - data: { - root: 'packages/jest', - name: 'jest', - targets: { - 'nx-release-publish': { - dependsOn: ['^nx-release-publish'], - executor: '@nx/js:release-publish', - options: { packageRoot: 'build/packages/jest' }, - configurations: {}, - }, - test: { - dependsOn: ['test-native', 'build-native', '^build-native'], - inputs: [ - 'default', - '^production', - '{workspaceRoot}/jest.preset.js', - ], - executor: '@nx/jest:jest', - outputs: ['{workspaceRoot}/coverage/{projectRoot}'], - cache: true, - options: { - jestConfig: 'packages/jest/jest.config.ts', - passWithNoTests: true, - }, - configurations: {}, - }, - 'build-base': { - dependsOn: ['^build-base', 'build-native'], - inputs: ['production', '^production'], - executor: '@nx/js:tsc', - outputs: ['{options.outputPath}'], - cache: true, - options: { - outputPath: 'build/packages/jest', - tsConfig: 'packages/jest/tsconfig.lib.json', - main: 'packages/jest/index.ts', - assets: [ - { - input: 'packages/jest', - glob: '**/@(files|files-angular)/**', - output: '/', - }, - { - input: 'packages/jest', - glob: '**/files/**/.gitkeep', - output: '/', - }, - ], - }, - configurations: {}, - }, - }, - }, - }, - selectedTargetGroup: 'build-base', - selectTargetGroup(targetGroup) { - console.log(targetGroup); - }, - } as TargetGroupsProps, -}; - -export const TestSelected: Story = { - args: { - project: { - name: 'jest', - type: 'lib', - data: { - root: 'packages/jest', - name: 'jest', - targets: { - 'nx-release-publish': { - dependsOn: ['^nx-release-publish'], - executor: '@nx/js:release-publish', - options: { packageRoot: 'build/packages/jest' }, - configurations: {}, - }, - test: { - dependsOn: ['test-native', 'build-native', '^build-native'], - inputs: [ - 'default', - '^production', - '{workspaceRoot}/jest.preset.js', - ], - executor: '@nx/jest:jest', - outputs: ['{workspaceRoot}/coverage/{projectRoot}'], - cache: true, - options: { - jestConfig: 'packages/jest/jest.config.ts', - passWithNoTests: true, - }, - configurations: {}, - }, - 'build-base': { - dependsOn: ['^build-base', 'build-native'], - inputs: ['production', '^production'], - executor: '@nx/js:tsc', - outputs: ['{options.outputPath}'], - cache: true, - options: { - outputPath: 'build/packages/jest', - tsConfig: 'packages/jest/tsconfig.lib.json', - main: 'packages/jest/index.ts', - assets: [ - { - input: 'packages/jest', - glob: '**/@(files|files-angular)/**', - output: '/', - }, - { - input: 'packages/jest', - glob: '**/files/**/.gitkeep', - output: '/', - }, - ], - }, - configurations: {}, - }, - }, - }, - }, - selectedTargetGroup: 'test', - selectTargetGroup(targetGroup) { - console.log(targetGroup); - }, - } as TargetGroupsProps, -}; diff --git a/graph/ui-project-details/src/lib/target-groups/target-groups.tsx b/graph/ui-project-details/src/lib/target-groups/target-groups.tsx deleted file mode 100644 index 15377c4bc17073..00000000000000 --- a/graph/ui-project-details/src/lib/target-groups/target-groups.tsx +++ /dev/null @@ -1,141 +0,0 @@ -/* eslint-disable @nx/enforce-module-boundaries */ -// nx-ignore-next-line -import type { ProjectGraphProjectNode } from '@nx/devkit'; -import { Fragment, useEffect, useState } from 'react'; -import { Listbox, Transition } from '@headlessui/react'; -import { TargetGroup } from '../target-group/target-group'; -import { groupTargets } from '../utils/group-targets'; -import { - mapDispatchToProps, - mapDispatchToPropsType, - mapStateToProps, - mapStateToPropsType, -} from './target-groups.state'; -import { connect } from 'react-redux'; -import { ChevronUpDownIcon, CheckIcon } from '@heroicons/react/24/outline'; - -export type TargetGroupsProps = mapStateToPropsType & - mapDispatchToPropsType & { - className?: string; - project: ProjectGraphProjectNode; - variant?: 'default' | 'compact'; - }; - -export function TargetGroupsComponent({ - className, - project, - variant, - selectedTargetGroup, - selectTargetGroup, -}: TargetGroupsProps) { - const [targetGroups, setTargetGroups] = useState>( - {} - ); - const isCompact = variant === 'compact'; - - useEffect(() => { - const groups = groupTargets(project); - setTargetGroups(groups); - }, [project]); - - if (!targetGroups || !Object.keys(targetGroups).length) { - return null; - } - - return ( - <> -
        - -
        -
          - {Object.keys(targetGroups).map((targetGroup) => { - return ( - selectTargetGroup(targetGroup)} - technologies={project.data.metadata?.technologies} - /> - ); - })} -
        - - ); -} - -// example from https://headlessui.com/react/listbox -export function TargetGroupsInSelectBox({ - targetGroupNames, - selectedTargetGroup, - selectTargetGroup, -}: { - targetGroupNames: string[]; - selectedTargetGroup: string; - selectTargetGroup: (name: string) => void; -}) { - return ( - -
        - - {selectedTargetGroup} - - - - - - {targetGroupNames.map((targetGroupName, index) => ( - - `relative cursor-default select-none py-2 pl-10 pr-4 ${ - active - ? 'bg-slate-50 dark:border-slate-700/60 dark:border-slate-300/10 dark:bg-slate-800' - : 'text-slate-500 dark:text-slate-400 ' - }` - } - value={targetGroupName} - > - {({ selected }) => ( - <> - - {targetGroupName} - - {selected ? ( - - - ) : null} - - )} - - ))} - - -
        -
        - ); -} - -export const TargetGroups = connect( - mapStateToProps, - mapDispatchToProps -)(TargetGroupsComponent); -export default TargetGroups; diff --git a/graph/ui-project-details/src/lib/utils/group-targets.ts b/graph/ui-project-details/src/lib/utils/group-targets.ts index 3866836a9fdb75..0e7c4b4cd64b7b 100644 --- a/graph/ui-project-details/src/lib/utils/group-targets.ts +++ b/graph/ui-project-details/src/lib/utils/group-targets.ts @@ -8,46 +8,28 @@ import type { ProjectGraphProjectNode } from '@nx/devkit'; * @param project * @returns */ -export function groupTargets( - project: ProjectGraphProjectNode -): Record { - let targetGroups = project.data.metadata?.targetGroups ?? {}; - const allTargetsInTargetGroups: string[] = Object.values(targetGroups).flat(); - const allTargets: string[] = Object.keys(project.data.targets ?? {}).sort( - sortNxReleasePublishLast - ); - allTargets.forEach((target) => { - if (!allTargetsInTargetGroups.includes(target)) { - targetGroups[target] = [target]; - } +export function groupTargets(project: ProjectGraphProjectNode): { + groups: Record; + targets: string[]; +} { + const targetGroups = project.data.metadata?.targetGroups ?? {}; + Object.entries(targetGroups).forEach(([group, targets]) => { + targetGroups[group] = targets.sort(sortNxReleasePublishLast); }); - return targetGroups; -} - -export function defaultSelectTargetGroup(project: ProjectGraphProjectNode) { - return Object.keys(groupTargets(project))[0]; + const allTargetsInTargetGroups: string[] = Object.values(targetGroups).flat(); + const targets: string[] = Object.keys(project.data.targets ?? {}) + .filter((target) => { + return !allTargetsInTargetGroups.includes(target); + }) + .sort(sortNxReleasePublishLast); + return { + groups: targetGroups ?? {}, + targets: targets ?? [], + }; } function sortNxReleasePublishLast(a: string, b: string) { if (a === 'nx-release-publish') return 1; if (b === 'nx-release-publish') return -1; - return 1; -} - -/** - * This funciton returns the target group for a given target - * If the target is not in a group, it will return the target name - * @param targetName - * @param project - * @returns - */ -export function getTargetGroupForTarget( - targetName: string, - project: ProjectGraphProjectNode -): string { - let targetGroups = project.data.metadata?.targetGroups ?? {}; - const foundTargetGroup = Object.keys(targetGroups).find((group) => - targetGroups[group].includes(targetName) - ); - return foundTargetGroup ?? targetName; + return a.localeCompare(b); } diff --git a/nx-dev/ui-markdoc/src/lib/tags/cards.component.tsx b/nx-dev/ui-markdoc/src/lib/tags/cards.component.tsx index 58b2aef0019015..db6f9d2fe83612 100644 --- a/nx-dev/ui-markdoc/src/lib/tags/cards.component.tsx +++ b/nx-dev/ui-markdoc/src/lib/tags/cards.component.tsx @@ -4,7 +4,7 @@ import { DocumentIcon, PlayCircleIcon, } from '@heroicons/react/24/outline'; -import { frameworkIcons } from '@nx/graph/ui-icons'; +import { Framework, frameworkIcons } from '@nx/graph/ui-icons'; import { cx } from '@nx/nx-dev/ui-primitives'; import { ReactNode } from 'react'; @@ -127,7 +127,7 @@ export function LinkCard({ } )} > - {icon && frameworkIcons[icon]?.image} + {icon && frameworkIcons[icon as Framework]?.image}
    )}
    diff --git a/nx.json b/nx.json index 0960f08ef02951..3aef72abe3750e 100644 --- a/nx.json +++ b/nx.json @@ -87,10 +87,7 @@ }, "build-native": { "inputs": ["native"], - "cache": true, - "metadata": { - "technologies": ["rust"] - } + "cache": true }, "build-base": { "dependsOn": ["^build-base", "build-native"], @@ -102,19 +99,13 @@ "main": "{projectRoot}/index.ts" }, "outputs": ["{options.outputPath}"], - "cache": true, - "metadata": { - "technologies": ["typescript"] - } + "cache": true }, "test-native": { "inputs": ["native"], "executor": "@monodon/rust:test", "options": {}, - "cache": true, - "metadata": { - "technologies": ["rust"] - } + "cache": true }, "test": { "dependsOn": ["test-native", "build-native", "^build-native"], @@ -151,10 +142,7 @@ "{projectRoot}/.storybook/**/*", "{projectRoot}/tsconfig.storybook.json" ], - "cache": true, - "metadata": { - "technologies": ["storybook"] - } + "cache": true }, "build-ng": { "cache": true diff --git a/packages/gradle/src/plugin/nodes.spec.ts b/packages/gradle/src/plugin/nodes.spec.ts index e56ce0b858a381..304a07d94af5a7 100644 --- a/packages/gradle/src/plugin/nodes.spec.ts +++ b/packages/gradle/src/plugin/nodes.spec.ts @@ -69,6 +69,11 @@ describe('@nx/gradle/plugin', () => { expect(nodes.projects.proj).toMatchInlineSnapshot(` { "metadata": { + "targetGroups": { + "Test": [ + "test", + ], + }, "technologies": [ "gradle", ], @@ -85,6 +90,11 @@ describe('@nx/gradle/plugin', () => { "default", "^production", ], + "metadata": { + "technologies": [ + "gradle", + ], + }, "options": { "cwd": "proj", }, @@ -124,6 +134,11 @@ describe('@nx/gradle/plugin', () => { expect(nodes.projects['nested/nested/proj']).toMatchInlineSnapshot(` { "metadata": { + "targetGroups": { + "Test": [ + "test", + ], + }, "technologies": [ "gradle", ], @@ -140,6 +155,11 @@ describe('@nx/gradle/plugin', () => { "default", "^production", ], + "metadata": { + "technologies": [ + "gradle", + ], + }, "options": { "cwd": "nested/nested/proj", }, diff --git a/packages/gradle/src/plugin/nodes.ts b/packages/gradle/src/plugin/nodes.ts index ee1ff87782aa9e..cdc8dbe7734f65 100644 --- a/packages/gradle/src/plugin/nodes.ts +++ b/packages/gradle/src/plugin/nodes.ts @@ -4,7 +4,6 @@ import { ProjectConfiguration, TargetConfiguration, readJsonFile, - workspaceRoot, writeJsonFile, } from '@nx/devkit'; import { calculateHashForCreateNodes } from '@nx/devkit/src/utils/calculate-hash-for-create-nodes'; @@ -42,7 +41,7 @@ export const calculatedTargets: Record< { name: string; targets: Record; - targetGroups: Record; + metadata: ProjectConfiguration['metadata']; } > = {}; @@ -51,7 +50,7 @@ function readTargetsCache(): Record< { name: string; targets: Record; - targetGroups: Record; + metadata: ProjectConfiguration['metadata']; } > { return readJsonFile(cachePath); @@ -63,7 +62,7 @@ export function writeTargetsToCache( { name: string; targets: Record; - targetGroups: Record; + metadata: ProjectConfiguration['metadata']; } > ) { @@ -88,12 +87,7 @@ export const createNodes: CreateNodes = [ calculatedTargets[hash] = targetsCache[hash]; return { projects: { - [projectRoot]: { - ...targetsCache[hash], - metadata: { - technologies: ['gradle'], - }, - }, + [projectRoot]: targetsCache[hash], }, }; } @@ -137,13 +131,7 @@ export const createNodes: CreateNodes = [ context, outputDirs ); - calculatedTargets[hash] = { - name: projectName, - targets, - targetGroups, - }; - - const project: Omit = { + const project = { name: projectName, targets, metadata: { @@ -151,6 +139,7 @@ export const createNodes: CreateNodes = [ technologies: ['gradle'], }, }; + calculatedTargets[hash] = project; return { projects: { @@ -195,6 +184,9 @@ function createGradleTargets( inputs: inputsMap[task.name], outputs: outputs ? [outputs] : undefined, dependsOn: dependsOnMap[task.name], + metadata: { + technologies: ['gradle'], + }, }; if (!targetGroups[task.type]) { targetGroups[task.type] = [];