Skip to content

Commit

Permalink
feat(graph): add atomizer label to target groups (#26622)
Browse files Browse the repository at this point in the history
## Current Behavior
Atomized Groups are treated just like any other groups in the PDV

## Expected Behavior
We want to let people know that something was created by the Atomizer
and also surface more information to users.
  • Loading branch information
MaxKless authored Jun 26, 2024
1 parent ce3f7f4 commit 6528da3
Show file tree
Hide file tree
Showing 28 changed files with 439 additions and 120 deletions.
38 changes: 4 additions & 34 deletions graph/client/src/app/external-api-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,40 +25,10 @@ export class ExternalApiImpl extends ExternalApi {
console.log('graphInteractionEventListener not registered.');
return;
}
if (type === 'file-click') {
const url = `${payload.sourceRoot}/${payload.file}`;
this.graphInteractionEventListener({
type: 'file-click',
payload: { url },
});
} else if (type === 'open-project-config') {
this.graphInteractionEventListener({
type: 'open-project-config',
payload,
});
} else if (type === 'run-task') {
this.graphInteractionEventListener({
type: 'run-task',
payload,
});
} else if (type === 'open-project-graph') {
this.graphInteractionEventListener({
type: 'open-project-graph',
payload,
});
} else if (type === 'open-task-graph') {
this.graphInteractionEventListener({
type: 'open-task-graph',
payload,
});
} else if (type === 'override-target') {
this.graphInteractionEventListener({
type: 'override-target',
payload,
});
} else {
console.log('unhandled event', type, payload);
}
this.graphInteractionEventListener({
type,
payload,
});
}
);

Expand Down
2 changes: 2 additions & 0 deletions graph/client/src/app/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ const projectDetailsLoader = async (
project: ProjectGraphProjectNode;
sourceMap: Record<string, string[]>;
errors?: GraphError[];
connectedToCloud?: boolean;
}> => {
const workspaceData = await workspaceDataLoader(selectedWorkspaceId);
const sourceMaps = await sourceMapsLoader(selectedWorkspaceId);
Expand All @@ -102,6 +103,7 @@ const projectDetailsLoader = async (
project,
sourceMap: sourceMaps[project.data.root],
errors: workspaceData.errors,
connectedToCloud: workspaceData.connectedToCloud,
};
};

Expand Down
3 changes: 1 addition & 2 deletions graph/client/src/app/ui-tooltips/graph-tooltip-display.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,7 @@ export function TooltipDisplay() {
externalApiService.postEvent({
type: 'file-click',
payload: {
sourceRoot: currentTooltip.props.sourceRoot,
file: url,
url: `${currentTooltip.props.sourceRoot}/${url}`,
},
})
: undefined;
Expand Down
17 changes: 9 additions & 8 deletions graph/project-details/src/lib/project-details-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ import {
import { ProjectDetailsHeader } from './project-details-header';

export function ProjectDetailsPage() {
const { project, sourceMap, hash, errors } = useRouteLoaderData(
'selectedProjectDetails'
) as {
hash: string;
project: ProjectGraphProjectNode;
sourceMap: Record<string, string[]>;
errors?: GraphError[];
};
const { project, sourceMap, hash, errors, connectedToCloud } =
useRouteLoaderData('selectedProjectDetails') as {
hash: string;
project: ProjectGraphProjectNode;
sourceMap: Record<string, string[]>;
errors?: GraphError[];
connectedToCloud?: boolean;
};

const { environment, watch, appConfig } = useEnvironmentConfig();

Expand Down Expand Up @@ -64,6 +64,7 @@ export function ProjectDetailsPage() {
project={project}
sourceMap={sourceMap}
errors={errors}
connectedToCloud={connectedToCloud}
></ProjectDetailsWrapper>
</div>
</div>
Expand Down
14 changes: 14 additions & 0 deletions graph/project-details/src/lib/project-details-wrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ interface ProjectDetailsProps {
project: ProjectGraphProjectNode;
sourceMap: Record<string, string[]>;
errors?: GraphError[];
connectedToCloud?: boolean;
}

export function ProjectDetailsWrapper({
project,
sourceMap,
errors,
connectedToCloud,
}: ProjectDetailsProps) {
const environment = useEnvironmentConfig()?.environment;
const externalApiService = getExternalApiService();
Expand Down Expand Up @@ -95,6 +97,14 @@ export function ProjectDetailsWrapper({
[externalApiService]
);

const handleNxConnect = useCallback(
() =>
externalApiService.postEvent({
type: 'nx-connect',
}),
[externalApiService]
);

const updateSearchParams = (
params: URLSearchParams,
targetNames?: string[]
Expand Down Expand Up @@ -162,6 +172,10 @@ export function ProjectDetailsWrapper({
viewInProjectGraphPosition={
environment === 'nx-console' ? 'bottom' : 'top'
}
connectedToCloud={connectedToCloud}
nxConnectCallback={
environment === 'nx-console' ? handleNxConnect : undefined
}
/>
<ErrorToast errors={errors} />
</>
Expand Down
4 changes: 2 additions & 2 deletions graph/shared/src/lib/external-api-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ export function getExternalApiService() {
}

export class ExternalApiService {
private subscribers: Set<(event: { type: string; payload: any }) => void> =
private subscribers: Set<(event: { type: string; payload?: any }) => void> =
new Set();

postEvent(event: { type: string; payload: any }) {
postEvent(event: { type: string; payload?: any }) {
this.subscribers.forEach((subscriber) => {
subscriber(event);
});
Expand Down
1 change: 1 addition & 0 deletions graph/ui-icons/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './lib/technology-icon';
export * from './lib/framework-icons';
export * from './lib/ nx-cloud-icon';
17 changes: 17 additions & 0 deletions graph/ui-icons/src/lib/ nx-cloud-icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { FC, SVGProps } from 'react';

export const NxCloudIcon: FC<SVGProps<SVGSVGElement>> = (props) => (
<svg
role="img"
xmlns="http://www.w3.org/2000/svg"
stroke="currentColor"
fill="transparent"
viewBox="0 0 24 24"
{...props}
>
<path
d="M22.167 7.167v-2.5a2.5 2.5 0 0 0-2.5-2.5h-15a2.5 2.5 0 0 0-2.5 2.5v15a2.5 2.5 0 0 0 2.5 2.5h2.5m15-15c-2.76 0-5 2.24-5 5s-2.24 5-5 5-5 2.24-5 5m15-15V19.59a2.577 2.577 0 0 1-2.576 2.576H7.167"
strokeWidth="2"
/>
</svg>
);
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ export interface ProjectDetailsProps {
sourceMap: Record<string, string[]>;
errors?: GraphError[];
variant?: 'default' | 'compact';
connectedToCloud?: boolean;
onViewInProjectGraph?: (data: { projectName: string }) => void;
onViewInTaskGraph?: (data: {
projectName: string;
targetName: string;
}) => void;
onRunTarget?: (data: { projectName: string; targetName: string }) => void;
nxConnectCallback?: () => void;
viewInProjectGraphPosition?: 'top' | 'bottom';
}

Expand All @@ -41,7 +43,9 @@ export const ProjectDetails = ({
onViewInProjectGraph,
onViewInTaskGraph,
onRunTarget,
nxConnectCallback,
viewInProjectGraphPosition = 'top',
connectedToCloud,
}: ProjectDetailsProps) => {
const projectData = project.data;
const isCompact = variant === 'compact';
Expand Down Expand Up @@ -161,6 +165,8 @@ export const ProjectDetails = ({
variant={variant}
onRunTarget={onRunTarget}
onViewInTaskGraph={onViewInTaskGraph}
connectedToCloud={connectedToCloud}
nxConnectCallback={nxConnectCallback}
/>
</div>
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,28 @@ import { TargetConfigurationGroupHeader } from '../target-configuration-details-
export interface TargetConfigurationGroupContainerProps {
targetGroupName: string;
targetsNumber: number;
nonAtomizedTarget?: string;
connectedToCloud?: boolean;
nxConnectCallback?: () => void;
children: React.ReactNode;
}

export function TargetConfigurationGroupContainer({
targetGroupName,
targetsNumber,
nonAtomizedTarget,
connectedToCloud,
nxConnectCallback,
children,
}: TargetConfigurationGroupContainerProps) {
return (
<div className="mb-4 w-full">
<TargetConfigurationGroupHeader
targetGroupName={targetGroupName}
targetsNumber={targetsNumber}
nonAtomizedTarget={nonAtomizedTarget}
connectedToCloud={connectedToCloud}
nxConnectCallback={nxConnectCallback}
className="sticky top-0 z-10 bg-white dark:bg-slate-900"
/>
<div className="rounded-md border border-slate-200 p-2 dark:border-slate-700">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,21 @@ export const Simple: Story = {
targetsNumber: 5,
},
};

export const AtomizerCloud: Story = {
args: {
targetGroupName: 'Target Group Name',
targetsNumber: 5,
nonAtomizedTarget: 'e2e',
connectedToCloud: true,
},
};

export const AtomizerNoCloud: Story = {
args: {
targetGroupName: 'Target Group Name',
targetsNumber: 5,
nonAtomizedTarget: 'e2e',
connectedToCloud: false,
},
};
Original file line number Diff line number Diff line change
@@ -1,25 +1,60 @@
import { AtomizerTooltip, Tooltip } from '@nx/graph/ui-tooltips';
import { Pill } from '../pill';
import { Square3Stack3DIcon } from '@heroicons/react/24/outline';

export interface TargetConfigurationGroupHeaderProps {
targetGroupName: string;
targetsNumber: number;
className?: string;
nonAtomizedTarget?: string;
connectedToCloud?: boolean;
nxConnectCallback?: () => void;
showIcon?: boolean;
}

export const TargetConfigurationGroupHeader = ({
targetGroupName,
targetsNumber,
nonAtomizedTarget,
connectedToCloud = true,
nxConnectCallback,
className = '',
}: TargetConfigurationGroupHeaderProps) => {
return (
<header className={`px-4 py-2 text-lg capitalize ${className}`}>
<header
className={`flex items-center gap-2 px-4 py-2 text-lg capitalize ${className}`}
>
{targetGroupName}{' '}
{nonAtomizedTarget && <Square3Stack3DIcon className="h-5 w-5" />}
<Pill
text={
targetsNumber.toString() +
(targetsNumber === 1 ? ' target' : ' targets')
}
/>
{nonAtomizedTarget && (
<Tooltip
openAction="hover"
strategy="fixed"
usePortal={true}
content={
(
<AtomizerTooltip
connectedToCloud={connectedToCloud}
nonAtomizedTarget={nonAtomizedTarget}
nxConnectCallback={nxConnectCallback}
/>
) as any
}
>
<span className="inline-flex">
<Pill
color={connectedToCloud ? 'grey' : 'yellow'}
text={'Atomizer'}
/>
</span>
</Tooltip>
)}
</header>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import type { ProjectGraphProjectNode } from '@nx/devkit';

import { TargetConfigurationDetailsListItem } from '../target-configuration-details-list-item/target-configuration-details-list-item';
import { TargetConfigurationGroupContainer } from '../target-configuration-details-group-container/target-configuration-details-group-container';
import { groupTargets } from '../utils/group-targets';
import {
getNonAtomizedTargetForGroup,
groupTargets,
} from '../utils/group-targets';
import { useMemo } from 'react';

export interface TargetConfigurationGroupListProps {
Expand All @@ -16,6 +19,8 @@ export interface TargetConfigurationGroupListProps {
projectName: string;
targetName: string;
}) => void;
nxConnectCallback?: () => void;
connectedToCloud?: boolean;
className?: string;
}

Expand All @@ -25,7 +30,9 @@ export function TargetConfigurationGroupList({
sourceMap,
onRunTarget,
onViewInTaskGraph,
nxConnectCallback,
className = '',
connectedToCloud,
}: TargetConfigurationGroupListProps) {
const targetsGroup = useMemo(() => groupTargets(project), [project]);
const hasGroups = useMemo(() => {
Expand All @@ -47,6 +54,12 @@ export function TargetConfigurationGroupList({
<TargetConfigurationGroupContainer
targetGroupName={targetGroupName}
targetsNumber={targets.length}
nonAtomizedTarget={getNonAtomizedTargetForGroup(
project,
targetGroupName
)}
connectedToCloud={connectedToCloud}
nxConnectCallback={nxConnectCallback}
key={targetGroupName}
>
<ul className={className}>
Expand Down
15 changes: 15 additions & 0 deletions graph/ui-project-details/src/lib/utils/group-targets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,18 @@ function sortNxReleasePublishLast(a: string, b: string) {
if (b === 'nx-release-publish') return -1;
return a.localeCompare(b);
}

export function getNonAtomizedTargetForGroup(
project: ProjectGraphProjectNode,
targetGroupName: string
): string | undefined {
const targetWithNonAtomizedEquivalent = project.data.metadata?.targetGroups?.[
targetGroupName
]?.find(
(target) => project.data.targets?.[target]?.metadata?.nonAtomizedTarget
);
return targetWithNonAtomizedEquivalent
? project.data.targets?.[targetWithNonAtomizedEquivalent]?.metadata
?.nonAtomizedTarget
: undefined;
}
1 change: 1 addition & 0 deletions graph/ui-tooltips/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export * from './lib/tooltip-button';
export * from './lib/property-info-tooltip';
export * from './lib/sourcemap-info-tooltip';
export * from './lib/external-link';
export * from './lib/atomizer-tooltip';
Loading

0 comments on commit 6528da3

Please sign in to comment.