Skip to content
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Meta, StoryObj } from "@storybook/react";

import { LinkedEndpointsMenu } from ".";

// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction
const meta: Meta<typeof LinkedEndpointsMenu> = {
title: "Navigation/ScopeBar/LinkedEndpointsMenu",
component: LinkedEndpointsMenu,
parameters: {
// More on how to position stories at: https://storybook.js.org/docs/react/configure/story-layout
layout: "fullscreen"
}
};

export default meta;

export const Default: StoryObj = {
args: {
endpoints: [
{
spanCodeObjectId: "span:codeObject",
displayName:
"testMethodCallasdasdsadsadasdasdasdasdasdasdasdsadasdsads",
environment: "TEST"
},
{
spanCodeObjectId: "span:codeObject2",
displayName: "restMethodCall",
environment: "local"
}
]
}
};
35 changes: 35 additions & 0 deletions src/components/Navigation/ScopeBar/LinkedEndpointsMenu/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { sendUserActionTrackingEvent } from "../../../../utils/actions/sendUserActionTrackingEvent";
import { HTTPClientIcon } from "../../../common/icons/HTTPClientIcon";
import { MenuList } from "../../common/MenuList";
import { LinkedEndpoint } from "../../SpanInfo/types";
import { trackingEvents } from "../../tracking";
import * as s from "./styles";
import { LinkedEndpointsMenuProps } from "./types";

export const LinkedEndpointsMenu = ({
endpoints,
onEndpointsClick
}: LinkedEndpointsMenuProps) => {
const handleMenuItemClick = (endpoint: LinkedEndpoint) => {
sendUserActionTrackingEvent(trackingEvents.CODE_LOCATION_SELECTED);
onEndpointsClick(endpoint);
};

return (
<s.Container>
<s.Title>This client assets calls the following endpoint:</s.Title>
<MenuList
items={endpoints.map((x) => ({
id: x.spanCodeObjectId,
customContent: (
<s.MenuItem onClick={() => handleMenuItemClick(x)}>
<HTTPClientIcon size={12} color="currentColor" />
<span>{x.displayName}</span>
</s.MenuItem>
),
disabled: false
}))}
/>
</s.Container>
);
};
41 changes: 41 additions & 0 deletions src/components/Navigation/ScopeBar/LinkedEndpointsMenu/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import styled from "styled-components";
import {
bodyRegularTypography,
footnoteRegularTypography
} from "../../../common/App/typographies";

export const Container = styled.div`
display: flex;
flex-direction: column;
gap: 4px;
`;

export const MenuItem = styled.div`
display: flex;
flex-direction: 4px;
padding: 0 8px;
gap: 4px;
align-items: center;
color: ${({ theme }) => theme.colors.v3.icon.brandSecondary};
cursor: pointer;

${bodyRegularTypography}

&:hover {
text-decoration: underline;
}

span {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
`;

export const Title = styled.div`
${footnoteRegularTypography}
color: ${({ theme }) => theme.colors.v3.text.tertiary};
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { LinkedEndpoint } from "../../SpanInfo/types";

export interface LinkedEndpointsMenuProps {
endpoints: LinkedEndpoint[];
onEndpointsClick: (endpoint: LinkedEndpoint) => void;
}
32 changes: 29 additions & 3 deletions src/components/Navigation/ScopeBar/ScopeBar.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,20 @@ const mockedCodeContext: CodeContext = {
export const Default: Story = {
args: {
scope: mockedScope,
codeContext: mockedCodeContext
codeContext: mockedCodeContext,
linkedEndpoints: [
{
spanCodeObjectId:
"span:codeObjectasdasdasdsadasdassadasdasdasdsadasdasdasdasdasdasdasdsadsadasdasd",
displayName: "testMethodCall",
environment: "TEST"
},
{
spanCodeObjectId: "span:codeObject2",
displayName: "restMethodCall",
environment: "local"
}
]
}
};

Expand All @@ -94,7 +107,19 @@ export const HasMultipleCodeLocations: Story = {
]
}
},
codeContext: mockedCodeContext
codeContext: mockedCodeContext,
linkedEndpoints: [
{
spanCodeObjectId: "span:codeObject",
displayName: "testMethodCallasdasdasdasdadasdsadasdsadsadasdasdsads",
environment: "TEST"
},
{
spanCodeObjectId: "span:codeObject2",
displayName: "restMethodCall",
environment: "local"
}
]
}
};

Expand All @@ -104,6 +129,7 @@ export const AlreadyAtCode: Story = {
codeContext: {
...mockedCodeContext,
methodId: mockedScope.span?.methodId ?? null
}
},
linkedEndpoints: []
}
};
51 changes: 50 additions & 1 deletion src/components/Navigation/ScopeBar/index.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
import { useEffect, useState } from "react";
import { history } from "../../../containers/Main/history";
import { isString } from "../../../typeGuards/isString";
import { changeScope } from "../../../utils/actions/changeScope";
import { sendUserActionTrackingEvent } from "../../../utils/actions/sendUserActionTrackingEvent";
import { trackingEvents as mainTrackingEvents } from "../../Main/tracking";
import { CodeDetails, Scope } from "../../common/App/types";
import { NewPopover } from "../../common/NewPopover";
import { ChainIcon } from "../../common/icons/14px/ChainIcon";
import { CrosshairIcon } from "../../common/icons/16px/CrosshairIcon";
import { MaximizeIcon } from "../../common/icons/16px/MaximizeIcon";
import { MinimizeIcon } from "../../common/icons/16px/MinimizeIcon";
import { EndpointIcon } from "../../common/icons/EndpointIcon";
import { NewIconButton } from "../../common/v3/NewIconButton";
import { Tooltip } from "../../common/v3/Tooltip";
import { LinkedEndpoint } from "../SpanInfo/types";
import { actions } from "../actions";
import { Popup } from "../common/Popup";
import { trackingEvents } from "../tracking";
import { CodeContext, GoToCodeLocationPayload } from "../types";
import { LinkedEndpointsMenu } from "./LinkedEndpointsMenu";
import { TargetButtonMenu } from "./TargetButtonMenu";
import * as s from "./styles";
import { ScopeBarProps } from "./types";
Expand Down Expand Up @@ -60,9 +64,12 @@ export const ScopeBar = ({
codeContext,
isExpanded,
onExpandCollapseChange,
isSpanInfoEnabled
isSpanInfoEnabled,
linkedEndpoints
}: ScopeBarProps) => {
const [isTargetButtonMenuOpen, setIsTargetButtonMenuOpen] = useState(false);
const [isLinkedEndpointsMenuOpen, setIsLinkedEndpointsMenuOpen] =
useState(false);

const location = history.getCurrentLocation();
const spanDisplayName = scope?.span?.displayName;
Expand Down Expand Up @@ -94,6 +101,8 @@ export const ScopeBar = ({
(scope.code.codeDetailsList.length > 1 ||
scope.code.relatedCodeDetailsList.length > 0);

const isLinkedEndpointsButtonEnabled = linkedEndpoints.length > 0;

useEffect(() => {
setIsTargetButtonMenuOpen(false);
}, [scope]);
Expand All @@ -108,6 +117,15 @@ export const ScopeBar = ({
setIsTargetButtonMenuOpen(false);
};

const handleLinkedEndpointsClick = (endpoint: LinkedEndpoint) => {
changeScope({
span: {
spanCodeObjectId: endpoint.spanCodeObjectId
}
});
setIsLinkedEndpointsMenuOpen(true);
};

const handleExpandCollapseButtonClick = () => {
if (isExpanded) {
sendUserActionTrackingEvent(
Expand All @@ -121,6 +139,11 @@ export const ScopeBar = ({
onExpandCollapseChange(!isExpanded);
};

const handleLinkedEndpointsButtonClick = () => {
sendUserActionTrackingEvent(trackingEvents.LINKED_ENDPOINTS_BUTTON_CLICKED);
setIsLinkedEndpointsMenuOpen(!isLinkedEndpointsMenuOpen);
};

const handleTargetButtonClick = () => {
sendUserActionTrackingEvent(trackingEvents.TARGET_BUTTON_CLICKED);
if (scope && scope.code.codeDetailsList.length === 1) {
Expand Down Expand Up @@ -160,6 +183,32 @@ export const ScopeBar = ({
</>
) : null}
</s.ScopeNameContainer>
{isLinkedEndpointsButtonEnabled && (
<NewPopover
content={
<s.LinkedEndpointsPopup height={"126px"}>
<LinkedEndpointsMenu
endpoints={linkedEndpoints}
onEndpointsClick={handleLinkedEndpointsClick}
/>
</s.LinkedEndpointsPopup>
}
onOpenChange={setIsLinkedEndpointsMenuOpen}
isOpen={isLinkedEndpointsMenuOpen}
placement={"bottom-start"}
width={"100%"}
>
<div>
<Tooltip title={"Click to see the target endpoint"}>
<NewIconButton
icon={ChainIcon}
onClick={handleLinkedEndpointsButtonClick}
buttonType={"secondaryBorderless"}
/>
</Tooltip>
</div>
</NewPopover>
)}
{isSpanInfoEnabled && (
<Tooltip title={isExpanded ? "Collapse" : "Expand"}>
<NewIconButton
Expand Down
5 changes: 5 additions & 0 deletions src/components/Navigation/ScopeBar/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
} from "../../common/App/typographies";
import { CopyButton } from "../../common/v3/CopyButton";
import { Bar } from "../common/Bar";
import { Popup } from "../common/Popup";

export const ScopeBar = styled(Bar)`
${bodyRegularTypography}
Expand Down Expand Up @@ -71,3 +72,7 @@ export const ScopeBarButton = styled.button`
color: ${({ theme }) => theme.colors.v3.icon.primary};
}
`;

export const LinkedEndpointsPopup = styled(Popup)`
margin: 4px 8px;
`;
2 changes: 2 additions & 0 deletions src/components/Navigation/ScopeBar/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Scope } from "../../common/App/types";
import { LinkedEndpoint } from "../SpanInfo/types";
import { CodeContext } from "../types";

export interface ScopeBarProps {
Expand All @@ -7,4 +8,5 @@ export interface ScopeBarProps {
isExpanded: boolean;
onExpandCollapseChange: (isExpanded: boolean) => void;
isSpanInfoEnabled: boolean;
linkedEndpoints: LinkedEndpoint[];
}
7 changes: 7 additions & 0 deletions src/components/Navigation/SpanInfo/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,18 @@ export interface GetHighlightsSpanInfoDataPayload {
};
}

export interface LinkedEndpoint {
spanCodeObjectId: string;
displayName: string;
environment: string;
}

export interface SpanInfoData {
displayName: string;
services: string[];
environments: Environment[];
assetTypeId: string;
firstSeen?: string;
lastSeen?: string;
linkedEndpoints?: LinkedEndpoint[];
}
5 changes: 5 additions & 0 deletions src/components/Navigation/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,11 @@ export const Navigation = () => {
isExpanded={isSpanInfoVisible}
onExpandCollapseChange={handleScopeDisplayNameExpandCollapseChange}
isSpanInfoEnabled={isSpanInfoEnabled}
linkedEndpoints={
spanInfo?.linkedEndpoints?.filter(
(x) => x.environment === environment?.id
) ?? []
}
/>
) : (
<EnvironmentBar
Expand Down
1 change: 1 addition & 0 deletions src/components/Navigation/tracking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const trackingEvents = addPrefix(
FORWARD_BUTTON_CLICKED: "forward button clicked",
HOME_BUTTON_CLICKED: "home button clicked",
TARGET_BUTTON_CLICKED: "target button clicked",
LINKED_ENDPOINTS_BUTTON_CLICKED: "linked endpoints button clicked",
CODE_LOCATION_SELECTED: "code location selected",
CODE_BUTTON_CLICKED: "code button clicked",
ASSET_SELECTED: "asset selected",
Expand Down
18 changes: 18 additions & 0 deletions src/components/common/icons/14px/ChainIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from "react";
import { useIconProps } from "../hooks";
import { IconProps } from "../types";

const ChainIconComponent = (props: IconProps) => {
const { size, color } = useIconProps(props);

return (
<svg width={size} height={size} viewBox="0 0 14 14" fill="none">
<g stroke={color} strokeLinecap="round">
<path d="M5.828 8.2c-.904-.908-.822-2.46.181-3.468l2.909-2.92C9.922.806 11.468.724 12.372 1.63c.904.906.822 2.46-.182 3.468l-1.454 1.46" />
<path d="M8.172 5.797c.904.907.822 2.46-.181 3.467l-1.455 1.46-1.454 1.46c-1.004 1.007-2.55 1.089-3.454.182-.904-.906-.822-2.46.182-3.467l1.454-1.46" />
</g>
</svg>
);
};

export const ChainIcon = React.memo(ChainIconComponent);
Loading