Skip to content

Commit

Permalink
chore(graph): add task graph tooltips (nrwl#13262)
Browse files Browse the repository at this point in the history
  • Loading branch information
philipjfulcher authored Nov 18, 2022
1 parent 5045206 commit ec85e1b
Show file tree
Hide file tree
Showing 18 changed files with 247 additions and 82 deletions.
25 changes: 15 additions & 10 deletions graph/client/.storybook/preview.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
import '../src/styles.scss';

import React from 'react';
import { MemoryRouter } from 'react-router-dom';

export const decorators = [
(Story, context) => {
return (
<div className="bg-white text-slate-500 dark:bg-slate-900 dark:text-slate-400">
{context.title.startsWith('Project Graph') ? (
<div className="flex justify-center">
<div className="relative flex h-full w-72 flex-col overflow-y-scroll pb-10 shadow-lg ring-1 ring-slate-900/10 ring-opacity-10 transition-all dark:ring-slate-300/10">
<Story />
<MemoryRouter initialEntries={['/']}>
<div className="bg-white text-slate-500 dark:bg-slate-900 dark:text-slate-400">
{context.title.startsWith('Project Graph') ? (
<div className="flex justify-center">
<div className="relative flex h-full w-72 flex-col overflow-y-scroll pb-10 shadow-lg ring-1 ring-slate-900/10 ring-opacity-10 transition-all dark:ring-slate-300/10">
<Story />
</div>
</div>
</div>
) : (
<Story />
)}
</div>
) : (
<Story />
)}
</div>
</MemoryRouter>
);
},
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Link, LinkProps, useSearchParams, To } from 'react-router-dom';
import React from 'react';

type LinkWithSearchParamsProps = LinkProps &
React.RefAttributes<HTMLAnchorElement>;

function LinkWithSearchParams(props: LinkWithSearchParamsProps) {
const [searchParams] = useSearchParams();

let to: To;
if (typeof props.to === 'object') {
to = { ...props.to, search: searchParams.toString() };
} else if (typeof props.to === 'string') {
to = { pathname: props.to, search: searchParams.toString() };
}
return <Link {...props} to={to} />;
}

export default LinkWithSearchParams;
27 changes: 18 additions & 9 deletions graph/client/src/app/ui-tooltips/graph-tooltip-display.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import Tippy from '@tippyjs/react';
import ProjectNodeToolTip from './project-node-tooltip';
import EdgeNodeTooltip from './edge-tooltip';
import ProjectEdgeNodeTooltip from './project-edge-tooltip';
import { selectValueByThemeStatic } from '../theme-resolver';
import { useSyncExternalStore } from 'use-sync-external-store/shim';
import { getTooltipService } from './tooltip-service';
import TaskNodeTooltip from './task-node-tooltip';

const tooltipService = getTooltipService();

Expand All @@ -12,16 +13,24 @@ export function TooltipDisplay() {
(callback) => tooltipService.subscribe(callback),
() => tooltipService.currentTooltip
);
let tooltipToRender;
if (currentTooltip) {
switch (currentTooltip.type) {
case 'projectNode':
tooltipToRender = <ProjectNodeToolTip {...currentTooltip.props} />;
break;
case 'projectEdge':
tooltipToRender = <ProjectEdgeNodeTooltip {...currentTooltip.props} />;
break;
case 'taskNode':
tooltipToRender = <TaskNodeTooltip {...currentTooltip.props} />;
break;
}
}

return currentTooltip ? (
return tooltipToRender ? (
<Tippy
content={
currentTooltip.type === 'node' ? (
<ProjectNodeToolTip {...currentTooltip.props}></ProjectNodeToolTip>
) : (
<EdgeNodeTooltip {...currentTooltip.props}></EdgeNodeTooltip>
)
}
content={tooltipToRender}
visible={true}
getReferenceClientRect={currentTooltip.ref.getBoundingClientRect}
theme={selectValueByThemeStatic('dark-nx', 'nx')}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import { ComponentMeta, ComponentStory } from '@storybook/react';
import { EdgeNodeTooltip, EdgeNodeTooltipProps } from './edge-tooltip';
import {
ProjectEdgeNodeTooltip,
ProjectEdgeNodeTooltipProps,
} from './project-edge-tooltip';
import Tippy from '@tippyjs/react';

export default {
component: EdgeNodeTooltip,
title: 'Tooltips/EdgeNodeTooltip',
} as ComponentMeta<typeof EdgeNodeTooltip>;
component: ProjectEdgeNodeTooltip,
title: 'Tooltips/ProjectEdgeNodeTooltip',
} as ComponentMeta<typeof ProjectEdgeNodeTooltip>;

const Template: ComponentStory<typeof EdgeNodeTooltip> = (args) => (
const Template: ComponentStory<typeof ProjectEdgeNodeTooltip> = (args) => (
<Tippy
content={<EdgeNodeTooltip {...args} />}
content={<ProjectEdgeNodeTooltip {...args} />}
visible={true}
theme="nx"
interactive={true}
Expand All @@ -25,4 +28,4 @@ Primary.args = {
target: 'lib1',
source: 'lib2',
fileDependencies: [{ fileName: 'some/file.ts' }],
} as EdgeNodeTooltipProps;
} as ProjectEdgeNodeTooltipProps;
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import Tag from '../ui-components/tag';

export interface EdgeNodeTooltipProps {
export interface ProjectEdgeNodeTooltipProps {
type: string;
source: string;
target: string;
fileDependencies: Array<{ fileName: string }>;
}

export function EdgeNodeTooltip({
export function ProjectEdgeNodeTooltip({
type,
source,
target,
fileDependencies,
}: EdgeNodeTooltipProps) {
}: ProjectEdgeNodeTooltipProps) {
return (
<div>
<h4 className={type !== 'implicit' ? 'mb-3' : ''}>
Expand Down Expand Up @@ -42,4 +42,4 @@ export function EdgeNodeTooltip({
);
}

export default EdgeNodeTooltip;
export default ProjectEdgeNodeTooltip;
19 changes: 13 additions & 6 deletions graph/client/src/app/ui-tooltips/project-node-tooltip.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { getProjectGraphService } from '../machines/get-services';
import { FlagIcon, MapPinIcon } from '@heroicons/react/24/solid';
import Tag from '../ui-components/tag';
import { TooltipButton } from './tooltip-button';

export interface ProjectNodeToolTipProps {
type: 'app' | 'lib' | 'e2e';
Expand Down Expand Up @@ -57,16 +58,22 @@ export function ProjectNodeToolTip({
</p>
) : null}
<div className="flex">
<button onClick={onFocus}>Focus</button>
<button onClick={onExclude}>Exclude</button>
<button className="flex flex-row items-center" onClick={onStartTrace}>
<TooltipButton onClick={onFocus}>Focus</TooltipButton>
<TooltipButton onClick={onExclude}>Exclude</TooltipButton>
<TooltipButton
className="flex flex-row items-center"
onClick={onStartTrace}
>
<MapPinIcon className="mr-2 h-5 w-5 text-slate-500"></MapPinIcon>
Start
</button>
<button className="flex flex-row items-center" onClick={onEndTrace}>
</TooltipButton>
<TooltipButton
className="flex flex-row items-center"
onClick={onEndTrace}
>
<FlagIcon className="mr-2 h-5 w-5 text-slate-500"></FlagIcon>
End
</button>
</TooltipButton>
</div>
</div>
);
Expand Down
27 changes: 27 additions & 0 deletions graph/client/src/app/ui-tooltips/task-node-tooltip.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { ComponentStory, ComponentMeta } from '@storybook/react';
import { TaskNodeTooltip } from './task-node-tooltip';
import Tippy from '@tippyjs/react';

const Story: ComponentMeta<typeof TaskNodeTooltip> = {
component: TaskNodeTooltip,
title: 'Tooltips/TaskNodeTooltip',
};
export default Story;

const Template: ComponentStory<typeof TaskNodeTooltip> = (args) => (
<Tippy
content={<TaskNodeTooltip {...args} />}
visible={true}
theme="nx"
interactive={true}
maxWidth="none"
>
<p></p>
</Tippy>
);

export const Primary = Template.bind({});
Primary.args = {
id: 'my-lib:build',
executor: '@nrwl/webpack:webpack',
};
31 changes: 31 additions & 0 deletions graph/client/src/app/ui-tooltips/task-node-tooltip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import Tag from '../ui-components/tag';
import { Link, useParams } from 'react-router-dom';
import { TooltipLinkButton } from './tooltip-button';
import { getEnvironmentConfig } from '../hooks/use-environment-config';

export interface TaskNodeTooltipProps {
id: string;
executor: string;
}

export function TaskNodeTooltip({ id, executor }: TaskNodeTooltipProps) {
const params = useParams();
const selectedWorkspaceId = params['selectedProjectId'];

const to = selectedWorkspaceId
? `/${selectedWorkspaceId}/tasks/${id}`
: `/tasks/${id}`;
return (
<div>
<h4>
<Tag className="mr-3">{executor}</Tag>
{id}
</h4>
<div className="mt-2 flex">
<TooltipLinkButton to={to}>Focus</TooltipLinkButton>
</div>
</div>
);
}

export default TaskNodeTooltip;
36 changes: 36 additions & 0 deletions graph/client/src/app/ui-tooltips/tooltip-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/* eslint-disable-next-line */
import { Link, LinkProps } from 'react-router-dom';
import { HTMLAttributes } from 'react';
import LinkWithSearchParams from '../ui-components/link-with-current-search-params';

const sharedClasses =
'inline-flex items-center rounded-md border border-slate-300 bg-slate-50 py-2 px-4 mt-2 mr-2 text-slate-500 hover:bg-slate-100 dark:border-slate-600 dark:bg-slate-800 dark:text-slate-300 hover:dark:bg-slate-700';

export function TooltipButton({
className,
children,
...rest
}: HTMLAttributes<HTMLButtonElement>) {
return (
<button className={`${sharedClasses} ${className}`} {...rest}>
{children}
</button>
);
}

export function TooltipLinkButton({
to,
className,
children,
...rest
}: LinkProps) {
return (
<LinkWithSearchParams
className={`${sharedClasses} ${className}`}
to={to}
{...rest}
>
{children}
</LinkWithSearchParams>
);
}
34 changes: 27 additions & 7 deletions graph/client/src/app/ui-tooltips/tooltip-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import { getGraphService } from '../machines/graph.service';

import { VirtualElement } from '@popperjs/core';
import { ProjectNodeToolTipProps } from './project-node-tooltip';
import { EdgeNodeTooltipProps } from './edge-tooltip';
import { ProjectEdgeNodeTooltipProps } from './project-edge-tooltip';
import { GraphService } from '@nrwl/graph/ui-graph';
import { TaskNodeTooltipProps } from './task-node-tooltip';

export class GraphTooltipService {
private subscribers: Set<Function> = new Set();
Expand All @@ -14,13 +15,18 @@ export class GraphTooltipService {
case 'GraphRegenerated':
this.hideAll();
break;
case 'NodeClick':
case 'ProjectNodeClick':
this.openProjectNodeToolTip(event.ref, {
id: event.data.id,
tags: event.data.tags,
type: event.data.type,
});
break;
case 'TaskNodeClick':
this.openTaskNodeTooltip(event.ref, {
...event.data,
});
break;
case 'EdgeClick':
this.openEdgeToolTip(event.ref, {
type: event.data.type,
Expand All @@ -34,16 +40,30 @@ export class GraphTooltipService {
}

currentTooltip:
| { ref: VirtualElement; type: 'node'; props: ProjectNodeToolTipProps }
| { ref: VirtualElement; type: 'edge'; props: EdgeNodeTooltipProps };
| {
ref: VirtualElement;
type: 'projectNode';
props: ProjectNodeToolTipProps;
}
| { ref: VirtualElement; type: 'taskNode'; props: TaskNodeTooltipProps }
| {
ref: VirtualElement;
type: 'projectEdge';
props: ProjectEdgeNodeTooltipProps;
};

openProjectNodeToolTip(ref: VirtualElement, props: ProjectNodeToolTipProps) {
this.currentTooltip = { type: 'node', ref, props };
this.currentTooltip = { type: 'projectNode', ref, props };
this.broadcastChange();
}

openTaskNodeTooltip(ref: VirtualElement, props: TaskNodeTooltipProps) {
this.currentTooltip = { type: 'taskNode', ref, props };
this.broadcastChange();
}

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

Expand Down
24 changes: 0 additions & 24 deletions graph/client/src/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -75,32 +75,8 @@ canvas {
margin: 0.375rem;
}

.tippy-box[data-theme~='nx'] button {
background-color: rgba(249, 250, 251, 1);
border-color: $gray;
border-width: 1px;
border-radius: 0.375rem;
color: rgba(107, 114, 128, 1);
margin: 0.375rem;
padding: 0.5rem 1rem;

&:hover {
background-color: rgba(243, 244, 246, 1);
}
}

.tippy-box[data-theme~='dark-nx'] {
@extend .tippy-box, [data-theme~='nx'];
background-color: #0f172a;
color: rgba(148, 163, 184, 1);

button {
background-color: #0f172a;
border-color: rgb(71, 85, 105, 1);
color: rgb(203, 213, 225, 1);
}

button:hover {
background-color: rgb(51, 65, 85, 1);
}
}
Loading

0 comments on commit ec85e1b

Please sign in to comment.