feat(dep-graph): launch path tracing (#9919)
This commit is contained in:
parent
5f12ce0f12
commit
3dc818f631
@ -1,6 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
getCheckedProjectItems,
|
getCheckedProjectItems,
|
||||||
getDeselectAllButton,
|
getDeselectAllButton,
|
||||||
|
getFocusButtonForProject,
|
||||||
getGroupByFolderCheckbox,
|
getGroupByFolderCheckbox,
|
||||||
getImageDownloadButton,
|
getImageDownloadButton,
|
||||||
getIncludeProjectsInPathButton,
|
getIncludeProjectsInPathButton,
|
||||||
@ -146,7 +147,7 @@ describe('dep-graph-client', () => {
|
|||||||
describe('focusing projects in sidebar', () => {
|
describe('focusing projects in sidebar', () => {
|
||||||
it('should select appropriate projects', () => {
|
it('should select appropriate projects', () => {
|
||||||
cy.contains('nx-dev').scrollIntoView().should('be.visible');
|
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);
|
getCheckedProjectItems().should('have.length', 11);
|
||||||
});
|
});
|
||||||
@ -154,7 +155,7 @@ describe('dep-graph-client', () => {
|
|||||||
|
|
||||||
describe('unfocus button', () => {
|
describe('unfocus button', () => {
|
||||||
it('should uncheck all project items', () => {
|
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();
|
getUnfocusProjectButton().click();
|
||||||
|
|
||||||
getUncheckedProjectItems().should('have.length', 62);
|
getUncheckedProjectItems().should('have.length', 62);
|
||||||
@ -182,12 +183,12 @@ describe('dep-graph-client', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should be shown when a project is selected', () => {
|
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');
|
getImageDownloadButton().should('not.have.class', 'opacity-0');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be hidden when no more projects are selected', () => {
|
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();
|
getDeselectAllButton().click();
|
||||||
getImageDownloadButton().should('have.class', 'opacity-0');
|
getImageDownloadButton().should('have.class', 'opacity-0');
|
||||||
});
|
});
|
||||||
@ -196,7 +197,7 @@ describe('dep-graph-client', () => {
|
|||||||
describe('setting url params', () => {
|
describe('setting url params', () => {
|
||||||
it('should set focused project', () => {
|
it('should set focused project', () => {
|
||||||
cy.contains('nx-dev').scrollIntoView().should('be.visible');
|
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');
|
cy.url().should('contain', 'focus=nx-dev');
|
||||||
});
|
});
|
||||||
|
|||||||
@ -29,3 +29,6 @@ export const getIncludeProjectsInPathButton = () =>
|
|||||||
|
|
||||||
export const getImageDownloadButton = () =>
|
export const getImageDownloadButton = () =>
|
||||||
cy.get('[data-cy=downloadImageButton]');
|
cy.get('[data-cy=downloadImageButton]');
|
||||||
|
|
||||||
|
export const getFocusButtonForProject = (projectName: string) =>
|
||||||
|
cy.get(`[data-cy="focus-button-${projectName}"]`);
|
||||||
|
|||||||
@ -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
|
// TODO: This test is getting flaky but was fixed by increasing the tick time between checks
|
||||||
// Figure out a better way to test this
|
// Figure out a better way to test this
|
||||||
it('should retain selected projects as new libs are created', () => {
|
it('should retain selected projects as new libs are created', () => {
|
||||||
cy.contains('existing-app-1').siblings('button').click();
|
cy.get('[data-project="existing-app-1"]').click();
|
||||||
cy.contains('existing-lib-1').siblings('button').click();
|
cy.get('[data-project="existing-lib-1"]').click();
|
||||||
|
|
||||||
cy.tick(6000);
|
cy.tick(6000);
|
||||||
|
|
||||||
|
|||||||
@ -40,6 +40,7 @@ export const initialContext: DepGraphContext = {
|
|||||||
tracing: {
|
tracing: {
|
||||||
start: null,
|
start: null,
|
||||||
end: null,
|
end: null,
|
||||||
|
algorithm: 'shortest',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -211,6 +212,15 @@ export const depGraphMachine = Machine<
|
|||||||
setSearchDepth: {
|
setSearchDepth: {
|
||||||
actions: ['setSearchDepth', 'notifyRouteSearchDepth'],
|
actions: ['setSearchDepth', 'notifyRouteSearchDepth'],
|
||||||
},
|
},
|
||||||
|
setTracingAlgorithm: {
|
||||||
|
actions: [
|
||||||
|
assign((ctx, event) => {
|
||||||
|
ctx.tracing.algorithm = event.algorithm;
|
||||||
|
}),
|
||||||
|
'notifyRouteTracing',
|
||||||
|
'notifyGraphTracing',
|
||||||
|
],
|
||||||
|
},
|
||||||
filterByText: {
|
filterByText: {
|
||||||
target: 'textFiltered',
|
target: 'textFiltered',
|
||||||
},
|
},
|
||||||
@ -280,6 +290,7 @@ export const depGraphMachine = Machine<
|
|||||||
type: 'notifyGraphTracing',
|
type: 'notifyGraphTracing',
|
||||||
start: ctx.tracing.start,
|
start: ctx.tracing.start,
|
||||||
end: ctx.tracing.end,
|
end: ctx.tracing.end,
|
||||||
|
algorithm: ctx.tracing.algorithm,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -385,6 +396,7 @@ export const depGraphMachine = Machine<
|
|||||||
type: 'notifyRouteTracing',
|
type: 'notifyRouteTracing',
|
||||||
start: ctx.tracing.start,
|
start: ctx.tracing.start,
|
||||||
end: ctx.tracing.end,
|
end: ctx.tracing.end,
|
||||||
|
algorithm: ctx.tracing.algorithm,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -110,8 +110,11 @@ export class GraphService {
|
|||||||
|
|
||||||
case 'notifyGraphTracing':
|
case 'notifyGraphTracing':
|
||||||
if (event.start && event.end) {
|
if (event.start && event.end) {
|
||||||
this.traceProjects(event.start, event.end);
|
if (event.algorithm === 'shortest') {
|
||||||
// this.traceAllProjects(event.start, event.end);
|
this.traceProjects(event.start, event.end);
|
||||||
|
} else {
|
||||||
|
this.traceAllProjects(event.start, event.end);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -390,13 +393,6 @@ export class GraphService {
|
|||||||
if (iterations >= 1000) {
|
if (iterations >= 1000) {
|
||||||
console.log('failsafe triggered!');
|
console.log('failsafe triggered!');
|
||||||
}
|
}
|
||||||
paths.forEach((currentPath) => {
|
|
||||||
console.log(
|
|
||||||
currentPath
|
|
||||||
.map((path) => path.map((element) => element.id()))
|
|
||||||
.join(' => ')
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
let finalCollection = this.traversalGraph.collection();
|
let finalCollection = this.traversalGraph.collection();
|
||||||
|
|
||||||
@ -413,11 +409,10 @@ export class GraphService {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(finalCollection.length);
|
|
||||||
|
|
||||||
finalCollection.union(finalCollection.ancestors());
|
finalCollection.union(finalCollection.ancestors());
|
||||||
console.log(finalCollection.map((element) => element.id()));
|
this.transferToRenderGraph(
|
||||||
this.transferToRenderGraph(finalCollection);
|
finalCollection.union(finalCollection.ancestors())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private transferToRenderGraph(elements: cy.Collection) {
|
private transferToRenderGraph(elements: cy.Collection) {
|
||||||
|
|||||||
@ -22,6 +22,8 @@ export interface GraphPerfReport {
|
|||||||
numNodes: number;
|
numNodes: number;
|
||||||
numEdges: number;
|
numEdges: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type TracingAlgorithmType = 'shortest' | 'all';
|
||||||
// The events that the machine handles
|
// The events that the machine handles
|
||||||
|
|
||||||
export type DepGraphUIEvents =
|
export type DepGraphUIEvents =
|
||||||
@ -40,6 +42,7 @@ export type DepGraphUIEvents =
|
|||||||
| { type: 'setTracingEnd'; projectName: string }
|
| { type: 'setTracingEnd'; projectName: string }
|
||||||
| { type: 'clearTraceStart' }
|
| { type: 'clearTraceStart' }
|
||||||
| { type: 'clearTraceEnd' }
|
| { type: 'clearTraceEnd' }
|
||||||
|
| { type: 'setTracingAlgorithm'; algorithm: TracingAlgorithmType }
|
||||||
| { type: 'setCollapseEdges'; collapseEdges: boolean }
|
| { type: 'setCollapseEdges'; collapseEdges: boolean }
|
||||||
| { type: 'setIncludeProjectsByPath'; includeProjectsByPath: boolean }
|
| { type: 'setIncludeProjectsByPath'; includeProjectsByPath: boolean }
|
||||||
| { type: 'incrementSearchDepth' }
|
| { type: 'incrementSearchDepth' }
|
||||||
@ -126,6 +129,7 @@ export type GraphRenderEvents =
|
|||||||
type: 'notifyGraphTracing';
|
type: 'notifyGraphTracing';
|
||||||
start: string;
|
start: string;
|
||||||
end: string;
|
end: string;
|
||||||
|
algorithm: TracingAlgorithmType;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type RouteEvents =
|
export type RouteEvents =
|
||||||
@ -156,7 +160,12 @@ export type RouteEvents =
|
|||||||
type: 'notifyRouteSelectAffected';
|
type: 'notifyRouteSelectAffected';
|
||||||
}
|
}
|
||||||
| { type: 'notifyRouteClearSelect' }
|
| { type: 'notifyRouteClearSelect' }
|
||||||
| { type: 'notifyRouteTracing'; start: string; end: string };
|
| {
|
||||||
|
type: 'notifyRouteTracing';
|
||||||
|
start: string;
|
||||||
|
end: string;
|
||||||
|
algorithm: TracingAlgorithmType;
|
||||||
|
};
|
||||||
|
|
||||||
export type AllEvents = DepGraphUIEvents | GraphRenderEvents | RouteEvents;
|
export type AllEvents = DepGraphUIEvents | GraphRenderEvents | RouteEvents;
|
||||||
|
|
||||||
@ -184,6 +193,7 @@ export interface DepGraphContext {
|
|||||||
tracing: {
|
tracing: {
|
||||||
start: string;
|
start: string;
|
||||||
end: string;
|
end: string;
|
||||||
|
algorithm: TracingAlgorithmType;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -39,6 +39,12 @@ function parseSearchParamsToEvents(searchParams: string): DepGraphUIEvents[] {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
break;
|
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':
|
case 'traceStart':
|
||||||
events.push({
|
events.push({
|
||||||
type: 'setTracingStart',
|
type: 'setTracingStart',
|
||||||
|
|||||||
@ -10,7 +10,8 @@ type ParamKeys =
|
|||||||
| 'select'
|
| 'select'
|
||||||
| 'collapseEdges'
|
| 'collapseEdges'
|
||||||
| 'traceStart'
|
| 'traceStart'
|
||||||
| 'traceEnd';
|
| 'traceEnd'
|
||||||
|
| 'traceAlgorithm';
|
||||||
type ParamRecord = Record<ParamKeys, string | null>;
|
type ParamRecord = Record<ParamKeys, string | null>;
|
||||||
|
|
||||||
function reduceParamRecordToQueryString(params: ParamRecord): string {
|
function reduceParamRecordToQueryString(params: ParamRecord): string {
|
||||||
@ -37,6 +38,7 @@ export const createRouteMachine = () => {
|
|||||||
select: params.get('select'),
|
select: params.get('select'),
|
||||||
traceStart: params.get('traceStart'),
|
traceStart: params.get('traceStart'),
|
||||||
traceEnd: params.get('traceEnd'),
|
traceEnd: params.get('traceEnd'),
|
||||||
|
traceAlgorithm: params.get('traceAlgorithm'),
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialContext = {
|
const initialContext = {
|
||||||
@ -61,6 +63,7 @@ export const createRouteMachine = () => {
|
|||||||
collapseEdges: null,
|
collapseEdges: null,
|
||||||
traceStart: null,
|
traceStart: null,
|
||||||
traceEnd: null,
|
traceEnd: null,
|
||||||
|
traceAlgorithm: null,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
always: {
|
always: {
|
||||||
@ -129,15 +132,17 @@ export const createRouteMachine = () => {
|
|||||||
},
|
},
|
||||||
notifyRouteTracing: {
|
notifyRouteTracing: {
|
||||||
actions: assign((ctx, event) => {
|
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.traceStart = event.start;
|
||||||
ctx.params.traceEnd = event.end;
|
ctx.params.traceEnd = event.end;
|
||||||
|
ctx.params.traceAlgorithm = event.algorithm;
|
||||||
|
|
||||||
ctx.params.focus = null;
|
ctx.params.focus = null;
|
||||||
ctx.params.select = null;
|
ctx.params.select = null;
|
||||||
} else {
|
} else {
|
||||||
ctx.params.traceStart = null;
|
ctx.params.traceStart = null;
|
||||||
ctx.params.traceEnd = null;
|
ctx.params.traceEnd = null;
|
||||||
|
ctx.params.traceAlgorithm = null;
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
import type { ProjectGraphProjectNode } from '@nrwl/devkit';
|
import type { ProjectGraphProjectNode } from '@nrwl/devkit';
|
||||||
import { DepGraphSelector } from '../hooks/use-dep-graph-selector';
|
import { DepGraphSelector } from '../hooks/use-dep-graph-selector';
|
||||||
import { WorkspaceLayout } from '../interfaces';
|
import { WorkspaceLayout } from '../interfaces';
|
||||||
import { GraphPerfReport } from './interfaces';
|
import { GraphPerfReport, TracingAlgorithmType } from './interfaces';
|
||||||
|
|
||||||
export const allProjectsSelector: DepGraphSelector<
|
export const allProjectsSelector: DepGraphSelector<
|
||||||
ProjectGraphProjectNode[]
|
ProjectGraphProjectNode[]
|
||||||
@ -52,4 +52,5 @@ export const hasAffectedProjectsSelector: DepGraphSelector<boolean> = (state) =>
|
|||||||
export const getTracingInfo: DepGraphSelector<{
|
export const getTracingInfo: DepGraphSelector<{
|
||||||
start: string;
|
start: string;
|
||||||
end: string;
|
end: string;
|
||||||
|
algorithm: TracingAlgorithmType;
|
||||||
}> = (state) => state.context.tracing;
|
}> = (state) => state.context.tracing;
|
||||||
|
|||||||
@ -13,6 +13,15 @@ export const tracingStateConfig: DepGraphStateNodeConfig = {
|
|||||||
'notifyRouteTracing',
|
'notifyRouteTracing',
|
||||||
'notifyGraphTracing',
|
'notifyGraphTracing',
|
||||||
],
|
],
|
||||||
|
exit: [
|
||||||
|
assign((ctx, event) => {
|
||||||
|
if (event.type !== 'setTracingStart' && event.type !== 'setTracingEnd') {
|
||||||
|
ctx.tracing.start = null;
|
||||||
|
ctx.tracing.end = null;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
'notifyRouteTracing',
|
||||||
|
],
|
||||||
on: {
|
on: {
|
||||||
clearTraceStart: {
|
clearTraceStart: {
|
||||||
actions: [
|
actions: [
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import ExperimentalFeature from './experimental-feature';
|
|
||||||
import { getDepGraphService } from './machines/dep-graph.service';
|
import { getDepGraphService } from './machines/dep-graph.service';
|
||||||
|
|
||||||
export interface ProjectNodeToolTipProps {
|
export interface ProjectNodeToolTipProps {
|
||||||
@ -53,38 +52,36 @@ function ProjectNodeToolTip({ type, id, tags }: ProjectNodeToolTipProps) {
|
|||||||
<div className="flex">
|
<div className="flex">
|
||||||
<button onClick={onFocus}>Focus</button>
|
<button onClick={onFocus}>Focus</button>
|
||||||
<button onClick={onExclude}>Exclude</button>
|
<button onClick={onExclude}>Exclude</button>
|
||||||
<ExperimentalFeature>
|
<button className="flex flex-row items-center" onClick={onStartTrace}>
|
||||||
<button className="flex flex-row items-center" onClick={onStartTrace}>
|
<svg
|
||||||
<svg
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
className="mr-2 h-5 w-5 text-slate-500"
|
||||||
className="mr-2 h-5 w-5 text-slate-500"
|
viewBox="0 0 20 20"
|
||||||
viewBox="0 0 20 20"
|
fill="currentColor"
|
||||||
fill="currentColor"
|
>
|
||||||
>
|
<path
|
||||||
<path
|
fillRule="evenodd"
|
||||||
fillRule="evenodd"
|
d="M5.05 4.05a7 7 0 119.9 9.9L10 18.9l-4.95-4.95a7 7 0 010-9.9zM10 11a2 2 0 100-4 2 2 0 000 4z"
|
||||||
d="M5.05 4.05a7 7 0 119.9 9.9L10 18.9l-4.95-4.95a7 7 0 010-9.9zM10 11a2 2 0 100-4 2 2 0 000 4z"
|
clipRule="evenodd"
|
||||||
clipRule="evenodd"
|
/>
|
||||||
/>
|
</svg>
|
||||||
</svg>
|
Start
|
||||||
Start
|
</button>
|
||||||
</button>
|
<button className="flex flex-row items-center" onClick={onEndTrace}>
|
||||||
<button className="flex flex-row items-center" onClick={onEndTrace}>
|
<svg
|
||||||
<svg
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
className="mr-2 h-5 w-5 text-slate-500"
|
||||||
className="mr-2 h-5 w-5 text-slate-500"
|
viewBox="0 0 20 20"
|
||||||
viewBox="0 0 20 20"
|
fill="currentColor"
|
||||||
fill="currentColor"
|
>
|
||||||
>
|
<path
|
||||||
<path
|
fillRule="evenodd"
|
||||||
fillRule="evenodd"
|
d="M3 6a3 3 0 013-3h10a1 1 0 01.8 1.6L14.25 8l2.55 3.4A1 1 0 0116 13H6a1 1 0 00-1 1v3a1 1 0 11-2 0V6z"
|
||||||
d="M3 6a3 3 0 013-3h10a1 1 0 01.8 1.6L14.25 8l2.55 3.4A1 1 0 0116 13H6a1 1 0 00-1 1v3a1 1 0 11-2 0V6z"
|
clipRule="evenodd"
|
||||||
clipRule="evenodd"
|
/>
|
||||||
/>
|
</svg>
|
||||||
</svg>
|
End
|
||||||
End
|
</button>
|
||||||
</button>
|
|
||||||
</ExperimentalFeature>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,14 +1,21 @@
|
|||||||
import { DocumentSearchIcon } from '@heroicons/react/solid';
|
import {
|
||||||
|
DocumentSearchIcon,
|
||||||
|
FlagIcon,
|
||||||
|
LocationMarkerIcon,
|
||||||
|
} from '@heroicons/react/solid';
|
||||||
// nx-ignore-next-line
|
// nx-ignore-next-line
|
||||||
import type { ProjectGraphNode } from '@nrwl/devkit';
|
import type { ProjectGraphNode } from '@nrwl/devkit';
|
||||||
import { useDepGraphService } from '../hooks/use-dep-graph';
|
import { useDepGraphService } from '../hooks/use-dep-graph';
|
||||||
import { useDepGraphSelector } from '../hooks/use-dep-graph-selector';
|
import { useDepGraphSelector } from '../hooks/use-dep-graph-selector';
|
||||||
import {
|
import {
|
||||||
allProjectsSelector,
|
allProjectsSelector,
|
||||||
|
getTracingInfo,
|
||||||
selectedProjectNamesSelector,
|
selectedProjectNamesSelector,
|
||||||
workspaceLayoutSelector,
|
workspaceLayoutSelector,
|
||||||
} from '../machines/selectors';
|
} from '../machines/selectors';
|
||||||
import { parseParentDirectoriesFromFilePath } from '../util';
|
import { parseParentDirectoriesFromFilePath } from '../util';
|
||||||
|
import { TracingAlgorithmType } from '../machines/interfaces';
|
||||||
|
import ExperimentalFeature from '../experimental-feature';
|
||||||
|
|
||||||
function getProjectsByType(type: string, projects: ProjectGraphNode[]) {
|
function getProjectsByType(type: string, projects: ProjectGraphNode[]) {
|
||||||
return projects
|
return projects
|
||||||
@ -23,6 +30,12 @@ interface SidebarProject {
|
|||||||
|
|
||||||
type DirectoryProjectRecord = Record<string, SidebarProject[]>;
|
type DirectoryProjectRecord = Record<string, SidebarProject[]>;
|
||||||
|
|
||||||
|
interface TracingInfo {
|
||||||
|
start: string;
|
||||||
|
end: string;
|
||||||
|
algorithm: TracingAlgorithmType;
|
||||||
|
}
|
||||||
|
|
||||||
function groupProjectsByDirectory(
|
function groupProjectsByDirectory(
|
||||||
projects: ProjectGraphNode[],
|
projects: ProjectGraphNode[],
|
||||||
selectedProjects: string[],
|
selectedProjects: string[],
|
||||||
@ -58,17 +71,24 @@ function ProjectListItem({
|
|||||||
project,
|
project,
|
||||||
toggleProject,
|
toggleProject,
|
||||||
focusProject,
|
focusProject,
|
||||||
|
startTrace,
|
||||||
|
endTrace,
|
||||||
|
tracingInfo,
|
||||||
}: {
|
}: {
|
||||||
project: SidebarProject;
|
project: SidebarProject;
|
||||||
toggleProject: (projectId: string, currentlySelected: boolean) => void;
|
toggleProject: (projectId: string, currentlySelected: boolean) => void;
|
||||||
focusProject: (projectId: string) => void;
|
focusProject: (projectId: string) => void;
|
||||||
|
startTrace: (projectId: string) => void;
|
||||||
|
endTrace: (projectId: string) => void;
|
||||||
|
tracingInfo: TracingInfo;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<li className="relative block cursor-default select-none py-1 pl-3 pr-9 text-xs text-slate-600 dark:text-slate-400">
|
<li className="relative block cursor-default select-none py-1 pl-2 pr-6 text-xs text-slate-600 dark:text-slate-400">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<button
|
<button
|
||||||
|
data-cy={`focus-button-${project.projectGraphNode.name}`}
|
||||||
type="button"
|
type="button"
|
||||||
className="flex rounded-md"
|
className="mr-1 flex rounded-md"
|
||||||
title="Focus on this library"
|
title="Focus on this library"
|
||||||
onClick={() => focusProject(project.projectGraphNode.name)}
|
onClick={() => focusProject(project.projectGraphNode.name)}
|
||||||
>
|
>
|
||||||
@ -76,9 +96,47 @@ function ProjectListItem({
|
|||||||
<DocumentSearchIcon className="h-5 w-5" />
|
<DocumentSearchIcon className="h-5 w-5" />
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<ExperimentalFeature>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="mr-1 flex rounded-md"
|
||||||
|
title="Start Trace"
|
||||||
|
onClick={() => startTrace(project.projectGraphNode.name)}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className={`${
|
||||||
|
tracingInfo.start === project.projectGraphNode.name
|
||||||
|
? 'ring-blue-nx-base'
|
||||||
|
: 'ring-slate-200 dark:ring-slate-600'
|
||||||
|
} flex items-center rounded-md bg-white p-1 font-medium shadow-sm ring-1 transition hover:bg-slate-50 dark:bg-slate-800 dark:text-slate-400 dark:ring-slate-600 hover:dark:bg-slate-700`}
|
||||||
|
>
|
||||||
|
<LocationMarkerIcon className="h-5 w-5" />
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="flex rounded-md"
|
||||||
|
title="End Trace"
|
||||||
|
onClick={() => endTrace(project.projectGraphNode.name)}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className={`${
|
||||||
|
tracingInfo.end === project.projectGraphNode.name
|
||||||
|
? 'ring-blue-nx-base'
|
||||||
|
: 'ring-slate-200 dark:ring-slate-600'
|
||||||
|
} flex items-center rounded-md bg-white p-1 font-medium shadow-sm ring-1 transition hover:bg-slate-50 dark:bg-slate-800 dark:text-slate-400 dark:ring-slate-600 hover:dark:bg-slate-700`}
|
||||||
|
>
|
||||||
|
<FlagIcon className="h-5 w-5" />
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</ExperimentalFeature>
|
||||||
|
|
||||||
<label
|
<label
|
||||||
className="ml-3 block w-full cursor-pointer truncate rounded-md p-2 font-mono font-normal transition hover:bg-slate-50 hover:dark:bg-slate-700"
|
className="ml-2 block w-full cursor-pointer truncate rounded-md p-2 font-mono font-normal transition hover:bg-slate-50 hover:dark:bg-slate-700"
|
||||||
data-project={project.projectGraphNode.name}
|
data-project={project.projectGraphNode.name}
|
||||||
|
title={project.projectGraphNode.name}
|
||||||
data-active={project.isSelected.toString()}
|
data-active={project.isSelected.toString()}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
toggleProject(project.projectGraphNode.name, project.isSelected)
|
toggleProject(project.projectGraphNode.name, project.isSelected)
|
||||||
@ -128,12 +186,18 @@ function SubProjectList({
|
|||||||
selectProject,
|
selectProject,
|
||||||
deselectProject,
|
deselectProject,
|
||||||
focusProject,
|
focusProject,
|
||||||
|
startTrace,
|
||||||
|
endTrace,
|
||||||
|
tracingInfo,
|
||||||
}: {
|
}: {
|
||||||
headerText: string;
|
headerText: string;
|
||||||
projects: SidebarProject[];
|
projects: SidebarProject[];
|
||||||
selectProject: (projectName: string) => void;
|
selectProject: (projectName: string) => void;
|
||||||
deselectProject: (projectName: string) => void;
|
deselectProject: (projectName: string) => void;
|
||||||
focusProject: (projectName: string) => void;
|
focusProject: (projectName: string) => void;
|
||||||
|
startTrace: (projectId: string) => void;
|
||||||
|
endTrace: (projectId: string) => void;
|
||||||
|
tracingInfo: TracingInfo;
|
||||||
}) {
|
}) {
|
||||||
let sortedProjects = [...projects];
|
let sortedProjects = [...projects];
|
||||||
sortedProjects.sort((a, b) => {
|
sortedProjects.sort((a, b) => {
|
||||||
@ -163,6 +227,9 @@ function SubProjectList({
|
|||||||
project={project}
|
project={project}
|
||||||
toggleProject={toggleProject}
|
toggleProject={toggleProject}
|
||||||
focusProject={focusProject}
|
focusProject={focusProject}
|
||||||
|
startTrace={startTrace}
|
||||||
|
endTrace={endTrace}
|
||||||
|
tracingInfo={tracingInfo}
|
||||||
></ProjectListItem>
|
></ProjectListItem>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@ -173,6 +240,7 @@ function SubProjectList({
|
|||||||
|
|
||||||
export function ProjectList() {
|
export function ProjectList() {
|
||||||
const depGraphService = useDepGraphService();
|
const depGraphService = useDepGraphService();
|
||||||
|
const tracingInfo = useDepGraphSelector(getTracingInfo);
|
||||||
|
|
||||||
function deselectProject(projectName: string) {
|
function deselectProject(projectName: string) {
|
||||||
depGraphService.send({ type: 'deselectProject', projectName });
|
depGraphService.send({ type: 'deselectProject', projectName });
|
||||||
@ -186,6 +254,14 @@ export function ProjectList() {
|
|||||||
depGraphService.send({ type: 'focusProject', projectName });
|
depGraphService.send({ type: 'focusProject', projectName });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function startTrace(projectName: string) {
|
||||||
|
depGraphService.send({ type: 'setTracingStart', projectName });
|
||||||
|
}
|
||||||
|
|
||||||
|
function endTrace(projectName: string) {
|
||||||
|
depGraphService.send({ type: 'setTracingEnd', projectName });
|
||||||
|
}
|
||||||
|
|
||||||
const projects = useDepGraphSelector(allProjectsSelector);
|
const projects = useDepGraphSelector(allProjectsSelector);
|
||||||
const workspaceLayout = useDepGraphSelector(workspaceLayoutSelector);
|
const workspaceLayout = useDepGraphSelector(workspaceLayoutSelector);
|
||||||
const selectedProjects = useDepGraphSelector(selectedProjectNamesSelector);
|
const selectedProjects = useDepGraphSelector(selectedProjectNamesSelector);
|
||||||
@ -229,6 +305,9 @@ export function ProjectList() {
|
|||||||
deselectProject={deselectProject}
|
deselectProject={deselectProject}
|
||||||
selectProject={selectProject}
|
selectProject={selectProject}
|
||||||
focusProject={focusProject}
|
focusProject={focusProject}
|
||||||
|
startTrace={startTrace}
|
||||||
|
endTrace={endTrace}
|
||||||
|
tracingInfo={tracingInfo}
|
||||||
></SubProjectList>
|
></SubProjectList>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@ -246,6 +325,9 @@ export function ProjectList() {
|
|||||||
deselectProject={deselectProject}
|
deselectProject={deselectProject}
|
||||||
selectProject={selectProject}
|
selectProject={selectProject}
|
||||||
focusProject={focusProject}
|
focusProject={focusProject}
|
||||||
|
startTrace={startTrace}
|
||||||
|
endTrace={endTrace}
|
||||||
|
tracingInfo={tracingInfo}
|
||||||
></SubProjectList>
|
></SubProjectList>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@ -263,6 +345,9 @@ export function ProjectList() {
|
|||||||
deselectProject={deselectProject}
|
deselectProject={deselectProject}
|
||||||
selectProject={selectProject}
|
selectProject={selectProject}
|
||||||
focusProject={focusProject}
|
focusProject={focusProject}
|
||||||
|
startTrace={startTrace}
|
||||||
|
endTrace={endTrace}
|
||||||
|
tracingInfo={tracingInfo}
|
||||||
></SubProjectList>
|
></SubProjectList>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import ShowHideProjects from './show-hide-projects';
|
|||||||
import TextFilterPanel from './text-filter-panel';
|
import TextFilterPanel from './text-filter-panel';
|
||||||
import ThemePanel from './theme-panel';
|
import ThemePanel from './theme-panel';
|
||||||
import TracingPanel from './tracing-panel';
|
import TracingPanel from './tracing-panel';
|
||||||
|
import { TracingAlgorithmType } from '../machines/interfaces';
|
||||||
|
|
||||||
export function Sidebar() {
|
export function Sidebar() {
|
||||||
const depGraphService = useDepGraphService();
|
const depGraphService = useDepGraphService();
|
||||||
@ -33,9 +34,8 @@ export function Sidebar() {
|
|||||||
const hasAffectedProjects = useDepGraphSelector(hasAffectedProjectsSelector);
|
const hasAffectedProjects = useDepGraphSelector(hasAffectedProjectsSelector);
|
||||||
const groupByFolder = useDepGraphSelector(groupByFolderSelector);
|
const groupByFolder = useDepGraphSelector(groupByFolderSelector);
|
||||||
const collapseEdges = useDepGraphSelector(collapseEdgesSelector);
|
const collapseEdges = useDepGraphSelector(collapseEdgesSelector);
|
||||||
const environment = useEnvironmentConfig();
|
|
||||||
|
|
||||||
const { showExperimentalFeatures } = environment.appConfig;
|
const isTracing = depGraphService.state.matches('tracing');
|
||||||
|
|
||||||
// const isTracing = depGraphService.state.matches('tracing');
|
// const isTracing = depGraphService.state.matches('tracing');
|
||||||
const tracingInfo = useDepGraphSelector(getTracingInfo);
|
const tracingInfo = useDepGraphSelector(getTracingInfo);
|
||||||
@ -98,6 +98,10 @@ export function Sidebar() {
|
|||||||
depGraphService.send({ type: 'clearTraceEnd' });
|
depGraphService.send({ type: 'clearTraceEnd' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setAlgorithm(algorithm: TracingAlgorithmType) {
|
||||||
|
depGraphService.send({ type: 'setTracingAlgorithm', algorithm: algorithm });
|
||||||
|
}
|
||||||
|
|
||||||
const updateTextFilter = useCallback(
|
const updateTextFilter = useCallback(
|
||||||
(textFilter: string) => {
|
(textFilter: string) => {
|
||||||
depGraphService.send({ type: 'filterByText', search: textFilter });
|
depGraphService.send({ type: 'filterByText', search: textFilter });
|
||||||
@ -153,15 +157,16 @@ export function Sidebar() {
|
|||||||
resetFocus={resetFocus}
|
resetFocus={resetFocus}
|
||||||
></FocusedProjectPanel>
|
></FocusedProjectPanel>
|
||||||
) : null}
|
) : null}
|
||||||
|
{isTracing ? (
|
||||||
<ExperimentalFeature>
|
|
||||||
<TracingPanel
|
<TracingPanel
|
||||||
start={tracingInfo.start}
|
start={tracingInfo.start}
|
||||||
end={tracingInfo.end}
|
end={tracingInfo.end}
|
||||||
|
algorithm={tracingInfo.algorithm}
|
||||||
|
setAlgorithm={setAlgorithm}
|
||||||
resetStart={resetTraceStart}
|
resetStart={resetTraceStart}
|
||||||
resetEnd={resetTraceEnd}
|
resetEnd={resetTraceEnd}
|
||||||
></TracingPanel>
|
></TracingPanel>
|
||||||
</ExperimentalFeature>
|
) : null}
|
||||||
|
|
||||||
<TextFilterPanel
|
<TextFilterPanel
|
||||||
includePath={includePath}
|
includePath={includePath}
|
||||||
|
|||||||
@ -5,22 +5,55 @@ import {
|
|||||||
XCircleIcon,
|
XCircleIcon,
|
||||||
} from '@heroicons/react/solid';
|
} from '@heroicons/react/solid';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
|
import { TracingAlgorithmType } from '../machines/interfaces';
|
||||||
|
|
||||||
export interface TracingPanelProps {
|
export interface TracingPanelProps {
|
||||||
start: string;
|
start: string;
|
||||||
end: string;
|
end: string;
|
||||||
|
algorithm: TracingAlgorithmType;
|
||||||
resetStart: () => void;
|
resetStart: () => void;
|
||||||
resetEnd: () => void;
|
resetEnd: () => void;
|
||||||
|
setAlgorithm: (algorithm: TracingAlgorithmType) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TracingPanel = memo(
|
export const TracingPanel = memo(
|
||||||
({ start, end, resetStart, resetEnd }: TracingPanelProps) => {
|
({
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
algorithm,
|
||||||
|
setAlgorithm,
|
||||||
|
resetStart,
|
||||||
|
resetEnd,
|
||||||
|
}: TracingPanelProps) => {
|
||||||
return (
|
return (
|
||||||
<div className="mt-10 px-4">
|
<div className="mt-10 px-4">
|
||||||
<div className="transition duration-200 ease-in-out group-hover:opacity-60">
|
<div className="transition duration-200 ease-in-out group-hover:opacity-60">
|
||||||
<h3 className="cursor-text pb-2 text-sm font-semibold uppercase tracking-wide text-slate-800 dark:text-slate-200 lg:text-xs">
|
<h3 className="cursor-text pb-2 text-sm font-semibold uppercase tracking-wide text-slate-800 dark:text-slate-200 lg:text-xs">
|
||||||
Tracing Path
|
Tracing Path
|
||||||
</h3>
|
</h3>
|
||||||
|
<div className="mb-3 flex cursor-pointer flex-row rounded-md border text-center text-xs dark:border-slate-600">
|
||||||
|
<button
|
||||||
|
onClick={() => setAlgorithm('shortest')}
|
||||||
|
className={`${
|
||||||
|
algorithm === 'shortest'
|
||||||
|
? 'border-blue-nx-base dark:border-slate-200'
|
||||||
|
: 'border-gray-300 dark:border-slate-600'
|
||||||
|
} flex-1 rounded-l-md border bg-slate-50 py-2 px-4 text-slate-500 hover:bg-slate-100 dark:border-slate-600 dark:bg-slate-800 dark:text-slate-300 hover:dark:bg-slate-700`}
|
||||||
|
>
|
||||||
|
<span>Shortest</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setAlgorithm('all')}
|
||||||
|
className={`${
|
||||||
|
algorithm === 'all'
|
||||||
|
? 'border-blue-nx-base dark:border-slate-200'
|
||||||
|
: 'border-gray-300 dark:border-slate-600'
|
||||||
|
} flex-1 rounded-r-md border bg-slate-50 py-2 px-4 text-slate-500 hover:bg-slate-100 dark:bg-slate-800 dark:text-slate-300 hover:dark:bg-slate-700`}
|
||||||
|
>
|
||||||
|
<span>All</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-row items-center truncate ">
|
<div className="flex flex-row items-center truncate ">
|
||||||
<LocationMarkerIcon className="mr-2 h-5 w-5 text-slate-500 dark:text-slate-400" />
|
<LocationMarkerIcon className="mr-2 h-5 w-5 text-slate-500 dark:text-slate-400" />
|
||||||
{start ? (
|
{start ? (
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user