Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(graph): add tasks to graph #13133

Merged
merged 1 commit into from
Nov 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion graph/client-e2e/src/support/app.po.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export const getSelectProjectsMessage = () => cy.get('#no-projects-chosen');
export const getSelectProjectsMessage = () =>
cy.get('[data-cy=no-projects-selected]');
export const getGraph = () => cy.get('#graph-container');
export const getSelectAllButton = () => cy.get('[data-cy=selectAllButton]');
export const getDeselectAllButton = () => cy.get('[data-cy=deselectAllButton]');
Expand Down
9 changes: 2 additions & 7 deletions graph/client/src/app/app.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { GlobalStateProvider } from './state.provider';
import { themeInit } from './theme-resolver';
import { rankDirInit } from './rankdir-resolver';
import {
createBrowserRouter,
RouterProvider,
createHashRouter,
RouterProvider,
} from 'react-router-dom';
import { routes } from './routes';
import { getEnvironmentConfig } from './hooks/use-environment-config';
Expand All @@ -22,11 +21,7 @@ if (environmentConfig.localMode === 'build') {
const router = routerCreate(routes);

export function App() {
return (
<GlobalStateProvider>
<RouterProvider router={router} />
</GlobalStateProvider>
);
return <RouterProvider router={router} />;
}

export default App;
13 changes: 13 additions & 0 deletions graph/client/src/app/external-api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { getProjectGraphService } from './machines/get-services';

export class ExternalApi {
projectGraphService = getProjectGraphService();

focusProject(projectName: string) {
this.projectGraphService.send({ type: 'focusProject', projectName });
}

enableExperimentalFeatures() {
window.appConfig.showExperimentalFeatures = true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useSelector } from '@xstate/react';
import { ProjectGraphState } from '../machines/interfaces';
import { getProjectGraphService } from '../../machines/get-services';

export type ProjectGraphSelector<T> = (
projectGraphState: ProjectGraphState
) => T;

export function useProjectGraphSelector<T>(
selectorFunc: ProjectGraphSelector<T>
): T {
const projectGraphService = getProjectGraphService();

return useSelector<typeof projectGraphService, T>(
projectGraphService,
selectorFunc
);
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { assign } from '@xstate/immer';
import { actions, send } from 'xstate';
import { DepGraphStateNodeConfig } from './interfaces';
import { ProjectGraphStateNodeConfig } from './interfaces';

export const customSelectedStateConfig: DepGraphStateNodeConfig = {
export const customSelectedStateConfig: ProjectGraphStateNodeConfig = {
entry: actions.choose([
{
cond: 'selectActionCannotBePersistedToRoute',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { assign } from '@xstate/immer';
import { send } from 'xstate';
import { DepGraphStateNodeConfig } from './interfaces';
import { ProjectGraphStateNodeConfig } from './interfaces';

export const focusedStateConfig: DepGraphStateNodeConfig = {
export const focusedStateConfig: ProjectGraphStateNodeConfig = {
entry: [
assign((ctx, event) => {
if (event.type !== 'focusProject') return;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { getGraphService } from './graph.service';
import { getGraphService } from '../../machines/graph.service';

export const graphActor = (callback, receive) => {
const graphService = getGraphService();

receive((e) => {
const { selectedProjectNames, perfReport } = graphService.handleEvent(e);
const { selectedProjectNames, perfReport } =
graphService.handleProjectEvent(e);
callback({
type: 'setSelectedProjectsFromGraph',
selectedProjectNames,
Expand Down
113 changes: 113 additions & 0 deletions graph/client/src/app/feature-projects/machines/interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { GraphPerfReport } from '../../interfaces';
// nx-ignore-next-line
import {
ProjectGraphDependency,
ProjectGraphProjectNode,
} from 'nx/src/config/project-graph';
import { ActionObject, ActorRef, State, StateNodeConfig } from 'xstate';
import { GraphRenderEvents, RouteEvents } from '../../machines/interfaces';

// The hierarchical schema for the states
export interface ProjectGraphSchema {
states: {
idle: {};
unselected: {};
focused: {};
textFiltered: {};
customSelected: {};
tracing: {};
};
}

export type TracingAlgorithmType = 'shortest' | 'all';

// The events that the machine handles
export type ProjectGraphMachineEvents =
| {
type: 'setSelectedProjectsFromGraph';
selectedProjectNames: string[];
perfReport: GraphPerfReport;
}
| { type: 'selectProject'; projectName: string }
| { type: 'deselectProject'; projectName: string }
| { type: 'selectProjects'; projectNames: string[] }
| { type: 'deselectProjects'; projectNames: string[] }
| { type: 'selectAll' }
| { type: 'deselectAll' }
| { type: 'selectAffected' }
| { type: 'setGroupByFolder'; groupByFolder: boolean }
| { type: 'setTracingStart'; projectName: string }
| { type: 'setTracingEnd'; projectName: string }
| { type: 'clearTraceStart' }
| { type: 'clearTraceEnd' }
| { type: 'setTracingAlgorithm'; algorithm: TracingAlgorithmType }
| { type: 'setCollapseEdges'; collapseEdges: boolean }
| { type: 'setIncludeProjectsByPath'; includeProjectsByPath: boolean }
| { type: 'incrementSearchDepth' }
| { type: 'decrementSearchDepth' }
| { type: 'setSearchDepthEnabled'; searchDepthEnabled: boolean }
| { type: 'setSearchDepth'; searchDepth: number }
| { type: 'focusProject'; projectName: string }
| { type: 'unfocusProject' }
| { type: 'filterByText'; search: string }
| { type: 'clearTextFilter' }
| {
type: 'notifyProjectGraphSetProjects';
projects: ProjectGraphProjectNode[];
dependencies: Record<string, ProjectGraphDependency[]>;
affectedProjects: string[];
workspaceLayout: {
libsDir: string;
appsDir: string;
};
}
| {
type: 'updateGraph';
projects: ProjectGraphProjectNode[];
dependencies: Record<string, ProjectGraphDependency[]>;
};

// The context (extended state) of the machine
export interface ProjectGraphContext {
projects: ProjectGraphProjectNode[];
dependencies: Record<string, ProjectGraphDependency[]>;
affectedProjects: string[];
selectedProjects: string[];
focusedProject: string | null;
textFilter: string;
includePath: boolean;
searchDepth: number;
searchDepthEnabled: boolean;
groupByFolder: boolean;
collapseEdges: boolean;
workspaceLayout: {
libsDir: string;
appsDir: string;
};
graphActor: ActorRef<GraphRenderEvents>;
routeSetterActor: ActorRef<RouteEvents>;
routeListenerActor: ActorRef<ProjectGraphMachineEvents>;
lastPerfReport: GraphPerfReport;
tracing: {
start: string;
end: string;
algorithm: TracingAlgorithmType;
};
}

export type ProjectGraphStateNodeConfig = StateNodeConfig<
ProjectGraphContext,
{},
ProjectGraphMachineEvents,
ActionObject<ProjectGraphContext, ProjectGraphMachineEvents>
>;

export type ProjectGraphState = State<
ProjectGraphContext,
ProjectGraphMachineEvents,
any,
{
value: any;
context: ProjectGraphContext;
}
>;
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
import { assign } from '@xstate/immer';
import { createMachine, Machine, send, spawn } from 'xstate';
import { createMachine, send, spawn } from 'xstate';
import { customSelectedStateConfig } from './custom-selected.state';
import { focusedStateConfig } from './focused.state';
import { graphActor } from './graph.actor';
import {
ProjectGraphContext,
ProjectGraphSchema,
ProjectGraphEvents,
} from './interfaces';
import { createRouteMachine } from './route-setter.machine';
import { createRouteMachine } from '../../machines/route-setter.machine';
import { textFilteredStateConfig } from './text-filtered.state';
import { tracingStateConfig } from './tracing.state';
import { unselectedStateConfig } from './unselected.state';
import { ProjectGraphContext, ProjectGraphMachineEvents } from './interfaces';

export const initialContext: ProjectGraphContext = {
projects: [],
Expand Down Expand Up @@ -46,11 +42,11 @@ export const initialContext: ProjectGraphContext = {

export const projectGraphMachine = createMachine<
ProjectGraphContext,
ProjectGraphEvents
ProjectGraphMachineEvents
>(
{
predictableActionArguments: true,
id: 'DepGraph',
id: 'project-graph',
initial: 'idle',
context: initialContext,
states: {
Expand Down
59 changes: 59 additions & 0 deletions graph/client/src/app/feature-projects/machines/selectors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// nx-ignore-next-line
import type { ProjectGraphProjectNode } from '@nrwl/devkit';
import { ProjectGraphSelector } from '../hooks/use-project-graph-selector';
import { GraphPerfReport, WorkspaceLayout } from '../../interfaces';
import { TracingAlgorithmType } from './interfaces';

export const allProjectsSelector: ProjectGraphSelector<
ProjectGraphProjectNode[]
> = (state) => state.context.projects;

export const workspaceLayoutSelector: ProjectGraphSelector<WorkspaceLayout> = (
state
) => state.context.workspaceLayout;

export const selectedProjectNamesSelector: ProjectGraphSelector<string[]> = (
state
) => state.context.selectedProjects;

export const projectIsSelectedSelector: ProjectGraphSelector<boolean> = (
state
) => state.context.selectedProjects.length > 0;

export const lastPerfReportSelector: ProjectGraphSelector<GraphPerfReport> = (
state
) => state.context.lastPerfReport;

export const focusedProjectNameSelector: ProjectGraphSelector<string> = (
state
) => state.context.focusedProject;

export const searchDepthSelector: ProjectGraphSelector<{
searchDepth: number;
searchDepthEnabled: boolean;
}> = (state) => ({
searchDepth: state.context.searchDepth,
searchDepthEnabled: state.context.searchDepthEnabled,
});

export const includePathSelector: ProjectGraphSelector<boolean> = (state) =>
state.context.includePath;

export const groupByFolderSelector: ProjectGraphSelector<boolean> = (state) =>
state.context.groupByFolder;

export const collapseEdgesSelector: ProjectGraphSelector<boolean> = (state) =>
state.context.collapseEdges;

export const textFilterSelector: ProjectGraphSelector<string> = (state) =>
state.context.textFilter;

export const hasAffectedProjectsSelector: ProjectGraphSelector<boolean> = (
state
) => state.context.affectedProjects.length > 0;

export const getTracingInfo: ProjectGraphSelector<{
start: string;
end: string;
algorithm: TracingAlgorithmType;
}> = (state) => state.context.tracing;
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { assign } from '@xstate/immer';
import { send } from 'xstate';
import { DepGraphStateNodeConfig } from './interfaces';
import { ProjectGraphStateNodeConfig } from './interfaces';

export const textFilteredStateConfig: DepGraphStateNodeConfig = {
export const textFilteredStateConfig: ProjectGraphStateNodeConfig = {
entry: [
assign((ctx, event) => {
if (event.type !== 'filterByText') return;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { assign } from '@xstate/immer';
import { DepGraphStateNodeConfig } from './interfaces';
import { ProjectGraphStateNodeConfig } from './interfaces';

export const tracingStateConfig: DepGraphStateNodeConfig = {
export const tracingStateConfig: ProjectGraphStateNodeConfig = {
entry: [
assign((ctx, event) => {
if (event.type === 'setTracingStart') {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { assign } from '@xstate/immer';
import { send, spawn } from 'xstate';
import { DepGraphStateNodeConfig } from './interfaces';
import { routeListener } from './route-listener.actor';
import { routeListener } from '../../machines/route-listener.actor';
import { ProjectGraphStateNodeConfig } from './interfaces';

export const unselectedStateConfig: DepGraphStateNodeConfig = {
export const unselectedStateConfig: ProjectGraphStateNodeConfig = {
entry: [
'notifyGraphHideAllProjects',
assign((ctx, event) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import { ComponentStory, ComponentMeta } from '@storybook/react';
import {
CollapseEdgesPanel,
CollapseEdgesPanelProps,
} from './collapse-edges-panel';
import { ComponentMeta, ComponentStory } from '@storybook/react';
import { CollapseEdgesPanel } from './collapse-edges-panel';

export default {
component: CollapseEdgesPanel,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import { ComponentStory, ComponentMeta } from '@storybook/react';
import {
FocusedProjectPanel,
FocusedProjectPanelProps,
} from './focused-project-panel';
import { ComponentMeta, ComponentStory } from '@storybook/react';
import { FocusedProjectPanel } from './focused-project-panel';

export default {
component: FocusedProjectPanel,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import { ComponentStory, ComponentMeta } from '@storybook/react';
import {
DisplayOptionsPanelProps,
GroupByFolderPanel,
} from './group-by-folder-panel';
import { ComponentMeta, ComponentStory } from '@storybook/react';
import { GroupByFolderPanel } from './group-by-folder-panel';

export default {
component: GroupByFolderPanel,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ComponentStory, ComponentMeta } from '@storybook/react';
import { ComponentMeta, ComponentStory } from '@storybook/react';
import RankDirPanel from './rankdir-panel';

export default {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Menu } from '@headlessui/react';
import {
ArrowsUpDownIcon,
ArrowsRightLeftIcon,
ArrowsUpDownIcon,
} from '@heroicons/react/24/outline';
import { useEffect, useState } from 'react';
import {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ComponentStory, ComponentMeta } from '@storybook/react';
import { SearchDepth, SearchDepthProps } from './search-depth';
import { ComponentMeta, ComponentStory } from '@storybook/react';
import { SearchDepth } from './search-depth';

export default {
component: SearchDepth,
Expand Down
Loading