feat(dep-graph): launch path tracing (#9919)

This commit is contained in:
Philip Fulcher 2022-04-21 02:30:13 -04:00 committed by GitHub
parent 5f12ce0f12
commit 3dc818f631
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 229 additions and 67 deletions

View File

@ -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');
}); });

View File

@ -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}"]`);

View File

@ -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);

View File

@ -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,
}; };
}, },
{ {

View File

@ -110,8 +110,11 @@ export class GraphService {
case 'notifyGraphTracing': case 'notifyGraphTracing':
if (event.start && event.end) { if (event.start && event.end) {
if (event.algorithm === 'shortest') {
this.traceProjects(event.start, event.end); this.traceProjects(event.start, event.end);
// this.traceAllProjects(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) {

View File

@ -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;
}; };
} }

View File

@ -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',

View File

@ -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;
} }
}), }),
}, },

View File

@ -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;

View File

@ -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: [

View File

@ -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,7 +52,6 @@ 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"
@ -84,7 +82,6 @@ function ProjectNodeToolTip({ type, id, tags }: ProjectNodeToolTipProps) {
</svg> </svg>
End End
</button> </button>
</ExperimentalFeature>
</div> </div>
</div> </div>
); );

View File

@ -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>
); );
})} })}

View File

@ -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}

View File

@ -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 ? (