diff --git a/dep-graph/client-e2e/src/integration/app.spec.ts b/dep-graph/client-e2e/src/integration/app.spec.ts index c0d1880630..d4c2c7c947 100644 --- a/dep-graph/client-e2e/src/integration/app.spec.ts +++ b/dep-graph/client-e2e/src/integration/app.spec.ts @@ -1,6 +1,7 @@ import { getCheckedProjectItems, getDeselectAllButton, + getFocusButtonForProject, getGroupByFolderCheckbox, getImageDownloadButton, getIncludeProjectsInPathButton, @@ -146,7 +147,7 @@ describe('dep-graph-client', () => { describe('focusing projects in sidebar', () => { it('should select appropriate projects', () => { cy.contains('nx-dev').scrollIntoView().should('be.visible'); - cy.get('[data-project="nx-dev"]').prev('button').click({ force: true }); + getFocusButtonForProject('nx-dev').click({ force: true }); getCheckedProjectItems().should('have.length', 11); }); @@ -154,7 +155,7 @@ describe('dep-graph-client', () => { describe('unfocus button', () => { it('should uncheck all project items', () => { - cy.get('[data-project="nx-dev"]').prev('button').click({ force: true }); + getFocusButtonForProject('nx-dev').click({ force: true }); getUnfocusProjectButton().click(); getUncheckedProjectItems().should('have.length', 62); @@ -182,12 +183,12 @@ describe('dep-graph-client', () => { }); it('should be shown when a project is selected', () => { - cy.get('[data-project="nx-dev"]').prev('button').click({ force: true }); + cy.get('[data-project="nx-dev"]').click({ force: true }); getImageDownloadButton().should('not.have.class', 'opacity-0'); }); it('should be hidden when no more projects are selected', () => { - cy.get('[data-project="nx-dev"]').prev('button').click({ force: true }); + cy.get('[data-project="nx-dev"]').click({ force: true }); getDeselectAllButton().click(); getImageDownloadButton().should('have.class', 'opacity-0'); }); @@ -196,7 +197,7 @@ describe('dep-graph-client', () => { describe('setting url params', () => { it('should set focused project', () => { cy.contains('nx-dev').scrollIntoView().should('be.visible'); - cy.get('[data-project="nx-dev"]').prev('button').click({ force: true }); + getFocusButtonForProject('nx-dev').click({ force: true }); cy.url().should('contain', 'focus=nx-dev'); }); diff --git a/dep-graph/client-e2e/src/support/app.po.ts b/dep-graph/client-e2e/src/support/app.po.ts index 7539317db6..25d230a03f 100644 --- a/dep-graph/client-e2e/src/support/app.po.ts +++ b/dep-graph/client-e2e/src/support/app.po.ts @@ -29,3 +29,6 @@ export const getIncludeProjectsInPathButton = () => export const getImageDownloadButton = () => cy.get('[data-cy=downloadImageButton]'); + +export const getFocusButtonForProject = (projectName: string) => + cy.get(`[data-cy="focus-button-${projectName}"]`); diff --git a/dep-graph/client-e2e/src/watch-mode-integration/watch-mode.spec.ts b/dep-graph/client-e2e/src/watch-mode-integration/watch-mode.spec.ts index e9ed387a4a..41661a68ab 100644 --- a/dep-graph/client-e2e/src/watch-mode-integration/watch-mode.spec.ts +++ b/dep-graph/client-e2e/src/watch-mode-integration/watch-mode.spec.ts @@ -23,8 +23,8 @@ describe('dep-graph-client in watch mode', () => { // TODO: This test is getting flaky but was fixed by increasing the tick time between checks // Figure out a better way to test this it('should retain selected projects as new libs are created', () => { - cy.contains('existing-app-1').siblings('button').click(); - cy.contains('existing-lib-1').siblings('button').click(); + cy.get('[data-project="existing-app-1"]').click(); + cy.get('[data-project="existing-lib-1"]').click(); cy.tick(6000); diff --git a/dep-graph/client/src/app/machines/dep-graph.machine.ts b/dep-graph/client/src/app/machines/dep-graph.machine.ts index e36b18ea2d..7ff221b1c2 100644 --- a/dep-graph/client/src/app/machines/dep-graph.machine.ts +++ b/dep-graph/client/src/app/machines/dep-graph.machine.ts @@ -40,6 +40,7 @@ export const initialContext: DepGraphContext = { tracing: { start: null, end: null, + algorithm: 'shortest', }, }; @@ -211,6 +212,15 @@ export const depGraphMachine = Machine< setSearchDepth: { actions: ['setSearchDepth', 'notifyRouteSearchDepth'], }, + setTracingAlgorithm: { + actions: [ + assign((ctx, event) => { + ctx.tracing.algorithm = event.algorithm; + }), + 'notifyRouteTracing', + 'notifyGraphTracing', + ], + }, filterByText: { target: 'textFiltered', }, @@ -280,6 +290,7 @@ export const depGraphMachine = Machine< type: 'notifyGraphTracing', start: ctx.tracing.start, end: ctx.tracing.end, + algorithm: ctx.tracing.algorithm, }; }, { @@ -385,6 +396,7 @@ export const depGraphMachine = Machine< type: 'notifyRouteTracing', start: ctx.tracing.start, end: ctx.tracing.end, + algorithm: ctx.tracing.algorithm, }; }, { diff --git a/dep-graph/client/src/app/machines/graph.ts b/dep-graph/client/src/app/machines/graph.ts index c8960ea3b2..5222827b72 100644 --- a/dep-graph/client/src/app/machines/graph.ts +++ b/dep-graph/client/src/app/machines/graph.ts @@ -110,8 +110,11 @@ export class GraphService { case 'notifyGraphTracing': if (event.start && event.end) { - this.traceProjects(event.start, event.end); - // this.traceAllProjects(event.start, event.end); + if (event.algorithm === 'shortest') { + this.traceProjects(event.start, event.end); + } else { + this.traceAllProjects(event.start, event.end); + } } break; } @@ -390,13 +393,6 @@ export class GraphService { if (iterations >= 1000) { console.log('failsafe triggered!'); } - paths.forEach((currentPath) => { - console.log( - currentPath - .map((path) => path.map((element) => element.id())) - .join(' => ') - ); - }); let finalCollection = this.traversalGraph.collection(); @@ -413,11 +409,10 @@ export class GraphService { } }); - console.log(finalCollection.length); - finalCollection.union(finalCollection.ancestors()); - console.log(finalCollection.map((element) => element.id())); - this.transferToRenderGraph(finalCollection); + this.transferToRenderGraph( + finalCollection.union(finalCollection.ancestors()) + ); } private transferToRenderGraph(elements: cy.Collection) { diff --git a/dep-graph/client/src/app/machines/interfaces.ts b/dep-graph/client/src/app/machines/interfaces.ts index 150966c0c1..a8ba471192 100644 --- a/dep-graph/client/src/app/machines/interfaces.ts +++ b/dep-graph/client/src/app/machines/interfaces.ts @@ -22,6 +22,8 @@ export interface GraphPerfReport { numNodes: number; numEdges: number; } + +export type TracingAlgorithmType = 'shortest' | 'all'; // The events that the machine handles export type DepGraphUIEvents = @@ -40,6 +42,7 @@ export type DepGraphUIEvents = | { type: 'setTracingEnd'; projectName: string } | { type: 'clearTraceStart' } | { type: 'clearTraceEnd' } + | { type: 'setTracingAlgorithm'; algorithm: TracingAlgorithmType } | { type: 'setCollapseEdges'; collapseEdges: boolean } | { type: 'setIncludeProjectsByPath'; includeProjectsByPath: boolean } | { type: 'incrementSearchDepth' } @@ -126,6 +129,7 @@ export type GraphRenderEvents = type: 'notifyGraphTracing'; start: string; end: string; + algorithm: TracingAlgorithmType; }; export type RouteEvents = @@ -156,7 +160,12 @@ export type RouteEvents = type: 'notifyRouteSelectAffected'; } | { type: 'notifyRouteClearSelect' } - | { type: 'notifyRouteTracing'; start: string; end: string }; + | { + type: 'notifyRouteTracing'; + start: string; + end: string; + algorithm: TracingAlgorithmType; + }; export type AllEvents = DepGraphUIEvents | GraphRenderEvents | RouteEvents; @@ -184,6 +193,7 @@ export interface DepGraphContext { tracing: { start: string; end: string; + algorithm: TracingAlgorithmType; }; } diff --git a/dep-graph/client/src/app/machines/route-listener.actor.ts b/dep-graph/client/src/app/machines/route-listener.actor.ts index edf83bc846..7587a47b95 100644 --- a/dep-graph/client/src/app/machines/route-listener.actor.ts +++ b/dep-graph/client/src/app/machines/route-listener.actor.ts @@ -39,6 +39,12 @@ function parseSearchParamsToEvents(searchParams: string): DepGraphUIEvents[] { }); } break; + case 'traceAlgorithm': + if (value === 'shortest' || value === 'all') { + // this needs to go before other tracing options or else the default of 'shortest' gets used + events.unshift({ type: 'setTracingAlgorithm', algorithm: value }); + } + break; case 'traceStart': events.push({ type: 'setTracingStart', diff --git a/dep-graph/client/src/app/machines/route-setter.machine.ts b/dep-graph/client/src/app/machines/route-setter.machine.ts index 5fb0e78659..acb66d776f 100644 --- a/dep-graph/client/src/app/machines/route-setter.machine.ts +++ b/dep-graph/client/src/app/machines/route-setter.machine.ts @@ -10,7 +10,8 @@ type ParamKeys = | 'select' | 'collapseEdges' | 'traceStart' - | 'traceEnd'; + | 'traceEnd' + | 'traceAlgorithm'; type ParamRecord = Record; function reduceParamRecordToQueryString(params: ParamRecord): string { @@ -37,6 +38,7 @@ export const createRouteMachine = () => { select: params.get('select'), traceStart: params.get('traceStart'), traceEnd: params.get('traceEnd'), + traceAlgorithm: params.get('traceAlgorithm'), }; const initialContext = { @@ -61,6 +63,7 @@ export const createRouteMachine = () => { collapseEdges: null, traceStart: null, traceEnd: null, + traceAlgorithm: null, }, }, always: { @@ -129,15 +132,17 @@ export const createRouteMachine = () => { }, notifyRouteTracing: { actions: assign((ctx, event) => { - if (event.start !== null && event.end !== null) { + if (event.start !== null && event.end !== null && event.algorithm) { ctx.params.traceStart = event.start; ctx.params.traceEnd = event.end; + ctx.params.traceAlgorithm = event.algorithm; ctx.params.focus = null; ctx.params.select = null; } else { ctx.params.traceStart = null; ctx.params.traceEnd = null; + ctx.params.traceAlgorithm = null; } }), }, diff --git a/dep-graph/client/src/app/machines/selectors.ts b/dep-graph/client/src/app/machines/selectors.ts index e5c8a2cfdb..e5861360bf 100644 --- a/dep-graph/client/src/app/machines/selectors.ts +++ b/dep-graph/client/src/app/machines/selectors.ts @@ -2,7 +2,7 @@ import type { ProjectGraphProjectNode } from '@nrwl/devkit'; import { DepGraphSelector } from '../hooks/use-dep-graph-selector'; import { WorkspaceLayout } from '../interfaces'; -import { GraphPerfReport } from './interfaces'; +import { GraphPerfReport, TracingAlgorithmType } from './interfaces'; export const allProjectsSelector: DepGraphSelector< ProjectGraphProjectNode[] @@ -52,4 +52,5 @@ export const hasAffectedProjectsSelector: DepGraphSelector = (state) => export const getTracingInfo: DepGraphSelector<{ start: string; end: string; + algorithm: TracingAlgorithmType; }> = (state) => state.context.tracing; diff --git a/dep-graph/client/src/app/machines/tracing.state.ts b/dep-graph/client/src/app/machines/tracing.state.ts index beb5c9bdb7..5eaba6b3e6 100644 --- a/dep-graph/client/src/app/machines/tracing.state.ts +++ b/dep-graph/client/src/app/machines/tracing.state.ts @@ -13,6 +13,15 @@ export const tracingStateConfig: DepGraphStateNodeConfig = { 'notifyRouteTracing', 'notifyGraphTracing', ], + exit: [ + assign((ctx, event) => { + if (event.type !== 'setTracingStart' && event.type !== 'setTracingEnd') { + ctx.tracing.start = null; + ctx.tracing.end = null; + } + }), + 'notifyRouteTracing', + ], on: { clearTraceStart: { actions: [ diff --git a/dep-graph/client/src/app/project-node-tooltip.tsx b/dep-graph/client/src/app/project-node-tooltip.tsx index e7073902a6..21a246e715 100644 --- a/dep-graph/client/src/app/project-node-tooltip.tsx +++ b/dep-graph/client/src/app/project-node-tooltip.tsx @@ -1,4 +1,3 @@ -import ExperimentalFeature from './experimental-feature'; import { getDepGraphService } from './machines/dep-graph.service'; export interface ProjectNodeToolTipProps { @@ -53,38 +52,36 @@ function ProjectNodeToolTip({ type, id, tags }: ProjectNodeToolTipProps) {
- - - - + +
); diff --git a/dep-graph/client/src/app/sidebar/project-list.tsx b/dep-graph/client/src/app/sidebar/project-list.tsx index f2253ecf36..67054ace2d 100644 --- a/dep-graph/client/src/app/sidebar/project-list.tsx +++ b/dep-graph/client/src/app/sidebar/project-list.tsx @@ -1,14 +1,21 @@ -import { DocumentSearchIcon } from '@heroicons/react/solid'; +import { + DocumentSearchIcon, + FlagIcon, + LocationMarkerIcon, +} from '@heroicons/react/solid'; // nx-ignore-next-line import type { ProjectGraphNode } from '@nrwl/devkit'; import { useDepGraphService } from '../hooks/use-dep-graph'; import { useDepGraphSelector } from '../hooks/use-dep-graph-selector'; import { allProjectsSelector, + getTracingInfo, selectedProjectNamesSelector, workspaceLayoutSelector, } from '../machines/selectors'; import { parseParentDirectoriesFromFilePath } from '../util'; +import { TracingAlgorithmType } from '../machines/interfaces'; +import ExperimentalFeature from '../experimental-feature'; function getProjectsByType(type: string, projects: ProjectGraphNode[]) { return projects @@ -23,6 +30,12 @@ interface SidebarProject { type DirectoryProjectRecord = Record; +interface TracingInfo { + start: string; + end: string; + algorithm: TracingAlgorithmType; +} + function groupProjectsByDirectory( projects: ProjectGraphNode[], selectedProjects: string[], @@ -58,17 +71,24 @@ function ProjectListItem({ project, toggleProject, focusProject, + startTrace, + endTrace, + tracingInfo, }: { project: SidebarProject; toggleProject: (projectId: string, currentlySelected: boolean) => void; focusProject: (projectId: string) => void; + startTrace: (projectId: string) => void; + endTrace: (projectId: string) => void; + tracingInfo: TracingInfo; }) { return ( -
  • +
  • + + + + + + +