Skip to content

Commit

Permalink
feat(graph): add clickable project edge file links in nx console (#18113
Browse files Browse the repository at this point in the history
)
  • Loading branch information
MaxKless authored Jul 25, 2023
1 parent 7aee21a commit f8068b7
Show file tree
Hide file tree
Showing 10 changed files with 81 additions and 9 deletions.
17 changes: 17 additions & 0 deletions graph/client/src/app/external-api.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { getRouter } from './get-router';
import { getProjectGraphService } from './machines/get-services';
import { ProjectGraphMachineEvents } from './feature-projects/machines/interfaces';
import { getGraphService } from './machines/graph.service';

export class ExternalApi {
_projectGraphService = getProjectGraphService();
Expand All @@ -13,17 +14,29 @@ export class ExternalApi {
});

router = getRouter();
graphService = getGraphService();

projectGraphService = {
send: (event: ProjectGraphMachineEvents) => {
this.handleLegacyProjectGraphEvent(event);
},
};

private fileClickCallbackListeners: ((url: string) => void)[] = [];

get depGraphService() {
return this.projectGraphService;
}

constructor() {
this.graphService.listen((event) => {
if (event.type === 'FileLinkClick') {
const url = `${event.sourceRoot}/${event.file}`;
this.fileClickCallbackListeners.forEach((cb) => cb(url));
}
});
}

focusProject(projectName: string) {
this.router.navigate(`/projects/${encodeURIComponent(projectName)}`);
}
Expand All @@ -42,6 +55,10 @@ export class ExternalApi {
window.appConfig.showExperimentalFeatures = false;
}

registerFileClickCallback(callback: (url: string) => void) {
this.fileClickCallbackListeners.push(callback);
}

private handleLegacyProjectGraphEvent(event: ProjectGraphMachineEvents) {
switch (event.type) {
case 'focusProject':
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type {
/* eslint-enable @nx/enforce-module-boundaries */
import { interpret } from 'xstate';
import { projectGraphMachine } from './project-graph.machine';
import { AppConfig } from '../../interfaces';

export const mockProjects: ProjectGraphProjectNode[] = [
{
Expand Down Expand Up @@ -96,7 +97,24 @@ export const mockDependencies: Record<string, ProjectGraphDependency[]> = {
'auth-lib': [],
};

const mockAppConfig: AppConfig = {
showDebugger: false,
showExperimentalFeatures: false,
workspaces: [
{
id: 'local',
label: 'local',
projectGraphUrl: 'assets/project-graphs/e2e.json',
taskGraphUrl: 'assets/task-graphs/e2e.json',
},
],
defaultWorkspaceId: 'local',
};

describe('dep-graph machine', () => {
beforeEach(() => {
window.appConfig = mockAppConfig;
});
describe('initGraph', () => {
it('should set projects, dependencies, and workspaceLayout', () => {
const result = projectGraphMachine.transition(
Expand Down
7 changes: 5 additions & 2 deletions graph/client/src/app/hooks/get-project-graph-data-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ let projectGraphService: ProjectGraphService;

export function getProjectGraphDataService() {
if (projectGraphService === undefined) {
if (window.environment === 'dev' || window.environment === 'nx-console') {
if (window.environment === 'dev') {
projectGraphService = new FetchProjectGraphService();
} else if (window.environment === 'watch') {
projectGraphService = new MockProjectGraphService();
} else if (window.environment === 'release') {
} else if (
window.environment === 'release' ||
window.environment === 'nx-console'
) {
if (window.localMode === 'build') {
projectGraphService = new LocalProjectGraphService();
} else {
Expand Down
6 changes: 4 additions & 2 deletions graph/client/src/app/machines/graph.service.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { GraphService } from '@nx/graph/ui-graph';
import { selectValueByThemeStatic } from '../theme-resolver';
import { getEnvironmentConfig } from '../hooks/use-environment-config';

let graphService: GraphService;

export function getGraphService(): GraphService {
const environment = getEnvironmentConfig();
if (!graphService) {
const darkModeEnabled = selectValueByThemeStatic(true, false);
graphService = new GraphService(
'cytoscape-graph',
selectValueByThemeStatic('dark', 'light')
selectValueByThemeStatic('dark', 'light'),
environment.environment === 'nx-console' ? 'nx-console' : undefined
);
}

Expand Down
9 changes: 8 additions & 1 deletion graph/ui-graph/src/lib/graph-interaction-events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,16 @@ interface BackgroundClickEvent {
type: 'BackgroundClick';
}

interface FileLinkClickEvent {
type: 'FileLinkClick';
sourceRoot: string;
file: string;
}

export type GraphInteractionEvents =
| ProjectNodeClickEvent
| EdgeClickEvent
| GraphRegeneratedEvent
| TaskNodeClickEvent
| BackgroundClickEvent;
| BackgroundClickEvent
| FileLinkClickEvent;
2 changes: 1 addition & 1 deletion graph/ui-graph/src/lib/graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export class GraphService {
constructor(
container: string | HTMLElement,
theme: 'light' | 'dark',
renderMode?: 'nx-console' | 'nx-docs',
public renderMode?: 'nx-console' | 'nx-docs',
rankDir: 'TB' | 'LR' = 'TB'
) {
use(cytoscapeDagre);
Expand Down
16 changes: 15 additions & 1 deletion graph/ui-graph/src/lib/tooltip-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,21 @@ export class GraphTooltipService {
});
break;
case 'EdgeClick':
const callback =
graph.renderMode === 'nx-console'
? (url) =>
graph.broadcast({
type: 'FileLinkClick',
sourceRoot: event.data.sourceRoot,
file: url,
})
: undefined;
this.openEdgeToolTip(event.ref, {
type: event.data.type,
target: event.data.target,
source: event.data.source,
fileDependencies: event.data.fileDependencies,
fileClickCallback: callback,
});
break;
}
Expand All @@ -57,7 +67,11 @@ export class GraphTooltipService {
}

openEdgeToolTip(ref: VirtualElement, props: ProjectEdgeNodeTooltipProps) {
this.currentTooltip = { type: 'projectEdge', ref, props };
this.currentTooltip = {
type: 'projectEdge',
ref,
props,
};
this.broadcastChange();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,6 @@ export class ProjectTraversalGraph {
projectNode.affected = affectedProjectIds.includes(project.name);

projectNodes.push(projectNode);

dependencies[project.name].forEach((dep) => {
if (filteredProjectNames.includes(dep.target)) {
const edge = new ProjectEdge(dep);
Expand Down
1 change: 1 addition & 0 deletions graph/ui-graph/src/lib/util-cytoscape/render-graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ export class RenderGraph {
type: edge.data('type'),
source: edge.source().id(),
target: edge.target().id(),
sourceRoot: edge.source().data('root'),
fileDependencies:
edge
.source()
Expand Down
13 changes: 12 additions & 1 deletion graph/ui-tooltips/src/lib/project-edge-tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export interface ProjectEdgeNodeTooltipProps {
target: string;
fileDependencies: Array<{ fileName: string }>;
description?: string;
fileClickCallback: (fileName: string) => void;
}

export function ProjectEdgeNodeTooltip({
Expand All @@ -14,6 +15,7 @@ export function ProjectEdgeNodeTooltip({
target,
fileDependencies,
description,
fileClickCallback,
}: ProjectEdgeNodeTooltipProps) {
return (
<div className="text-sm text-slate-700 dark:text-slate-400">
Expand All @@ -33,7 +35,16 @@ export function ProjectEdgeNodeTooltip({
{fileDependencies.map((fileDep) => (
<li
key={fileDep.fileName}
className="whitespace-nowrap px-4 py-2 text-sm font-medium text-slate-800 dark:text-slate-300"
className={`whitespace-nowrap px-4 py-2 text-sm font-medium text-slate-800 dark:text-slate-300 ${
fileClickCallback !== undefined
? 'hover:underline hover:cursor-pointer'
: ''
}`}
onClick={
fileClickCallback !== undefined
? () => fileClickCallback(fileDep.fileName)
: () => {}
}
>
<span className="block truncate font-normal">
{fileDep.fileName}
Expand Down

1 comment on commit f8068b7

@vercel
Copy link

@vercel vercel bot commented on f8068b7 Jul 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

nx-dev – ./

nx-dev-git-master-nrwl.vercel.app
nx-five.vercel.app
nx.dev
nx-dev-nrwl.vercel.app

Please sign in to comment.