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,39 @@
import { Meta, StoryObj } from "@storybook/react";
import { AffectedEndpointsSelector } from ".";

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

export default meta;

type Story = StoryObj<typeof meta>;

export const Default: Story = {
args: {
value: "spanCodeObjectId1",
options: [
{
route: "test",
serviceName: "test1",
spanCodeObjectId: "spanCodeObjectId1"
},
{
route: "test",
serviceName: "test1",
spanCodeObjectId: "spanCodeObjectId2"
},
{
route: "test",
serviceName: "test1",
spanCodeObjectId: "spanCodeObjectId2"
}
]
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Link } from "../../../../../common/v3/Link";
import { Tooltip } from "../../../../../common/v3/Tooltip";
import * as s from "./styles";
import { EndpointOptionProps } from "./types";

export const EndpointOption = ({
serviceName,
route,
spanCodeObjectId,
onSpanLinkClick,
selected,
hideCopyIcon,
onClick
}: EndpointOptionProps) => {
const title = `${serviceName} ${route}`;

return (
<s.Container>
<Tooltip title={title}>
<s.EndpointName
$selected={selected}
$clickable={Boolean(onClick)}
onClick={() => onClick && onClick()}
>
<s.ServiceName>{serviceName}</s.ServiceName>

{spanCodeObjectId && onSpanLinkClick ? (
<Link onClick={() => onSpanLinkClick(spanCodeObjectId)}>
{route}
</Link>
) : (
<span>{route}</span>
)}
</s.EndpointName>
</Tooltip>
{!hideCopyIcon && <s.StyledCopyButton text={route} />}
</s.Container>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import styled, { css } from "styled-components";
import { footnoteRegularTypography } from "../../../../../common/App/typographies";
import { CopyButton } from "../../../../../common/v3/CopyButton";
import { EndpointNameProps } from "./types";

export const StyledCopyButton = styled(CopyButton)`
display: none;
padding: 0;
`;

export const Container = styled.div`
${footnoteRegularTypography}

display: flex;
gap: 4px;
align-items: center;
width: 100%;

&:hover {
${StyledCopyButton} {
display: flex;
}
}
`;

export const EndpointName = styled.div<EndpointNameProps>`
display: flex;
gap: 4px;
overflow: hidden;
border-radius: 4px;
${({ $selected }) => {
if (!$selected) {
return css`
width: 100%;
padding: 4px 8px;
${ServiceName} {
width: 151px;
}
`;
}
return null;
}}

${({ $clickable }) =>
$clickable
? css`
&:hover {
background: ${({ theme }) => theme.colors.v3.surface.highlight};
}
`
: ""}
`;

export const ServiceName = styled.span`
max-width: 50%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: ${({ theme }) => theme.colors.v3.text.tertiary};
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export interface EndpointOptionProps {
serviceName: string;
route: string;
spanCodeObjectId?: string;
selected?: boolean;
onSpanLinkClick?: (spanCodeObjectId: string) => void;
hideCopyIcon?: boolean;
onClick?: ((spanCodeObjectId?: string) => void) | null;
}

export interface EndpointNameProps {
$selected?: boolean;
$clickable?: boolean;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { ReactNode } from "react";
import { trimEndpointScheme } from "../../../../../utils/trimEndpointScheme";
import { Select } from "../insightCards/common/InsightCard/Select";
import { CustomContentProps } from "../insightCards/common/InsightCard/Select/types";
import { EndpointOption } from "./EndpointOption";
import * as s from "./styles";
import { AffectedEndpointsSelectorProps, Option } from "./types";

const renderOptions = (
endpoints: Option[],
handleLinkClick: (spanCodeObjectId?: string) => void
): {
label: string;
customContent: (props: CustomContentProps) => ReactNode;
value: string;
}[] =>
endpoints.map((x) => {
const spanCodeObjectId = x.spanCodeObjectId;
const route = trimEndpointScheme(x.route);
return {
label: route,
customContent: ({ isSelected, onClick }) => (
<EndpointOption
serviceName={x.serviceName}
route={route}
selected={isSelected}
spanCodeObjectId={spanCodeObjectId}
onSpanLinkClick={handleLinkClick}
hideCopyIcon={!isSelected}
onClick={onClick}
/>
),
value: spanCodeObjectId
};
});

export const AffectedEndpointsSelector = ({
onAssetLinkClick,
insightType,
value,
options,
onChange
}: AffectedEndpointsSelectorProps) => {
const handleSpanLinkClick = (spanCodeObjectId?: string) => {
spanCodeObjectId && onAssetLinkClick(spanCodeObjectId, insightType);
};

return (
<Select
value={value}
onChange={(selectedOption) => {
const selected =
options.find((x) => x.spanCodeObjectId === selectedOption) ?? null;

onChange(selected);
}}
options={renderOptions(options, handleSpanLinkClick)}
listHeader={
<s.ListHeader>
<EndpointOption
route="Endpoint"
serviceName="Service"
hideCopyIcon={true}
/>
</s.ListHeader>
}
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import styled from "styled-components";
import { footnoteRegularTypography } from "../../../../common/App/typographies";

export const ListHeader = styled.div`
display: flex;
gap: 4px;
color: ${({ theme }) => theme.colors.v3.text.tertiary};

${footnoteRegularTypography}
`;

export const ServicePart = styled.div`
width: 151px;
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { InsightType } from "../../../../../types";

export interface FilterButtonProps {
isActive: boolean;
onClick: () => void;
filterCount: number;
}

export interface ContainerProps {
$isActive: boolean;
}

export interface AffectedEndpointsSelectorProps {
insightType: InsightType;
value?: string;
options: Option[];
onChange: (selected: Option | null) => void;

onAssetLinkClick: (
spanCodeObjectId: string,
insightType: InsightType
) => void;
}

export interface Option {
serviceName: string;
route: string;
spanCodeObjectId: string;
}
Original file line number Diff line number Diff line change
@@ -1,41 +1,18 @@
import { ReactNode, useState } from "react";
import { useState } from "react";
import { useGlobalStore } from "../../../../../../containers/Main/stores/useGlobalStore";
import { getDurationString } from "../../../../../../utils/getDurationString";
import { trimEndpointScheme } from "../../../../../../utils/trimEndpointScheme";
import { TraceIcon } from "../../../../../common/icons/12px/TraceIcon";
import { Button } from "../../../../../common/v3/Button";
import { Tooltip } from "../../../../../common/v3/Tooltip";
import { InsightType, NPlusOneEndpointInfo, Trace } from "../../../../types";
import { InsightType, Trace } from "../../../../types";
import { AffectedEndpointsSelector } from "../../AffectedEndpointsSelector";
import { InsightCard } from "../common/InsightCard";
import { ColumnsContainer } from "../common/InsightCard/ColumnsContainer";
import { EndpointSelectedOption } from "../common/InsightCard/EndpointSelectedOption";
import { KeyValue } from "../common/InsightCard/KeyValue";
import { Select } from "../common/InsightCard/Select";
import { ContentContainer, Description, Details } from "../styles";
import * as s from "./styles";
import { SpaNPlusOneInsightCardProps } from "./types";

const renderOptions = (
endpoints: NPlusOneEndpointInfo[],
handleLinkClick: (spanCodeObjectId?: string) => void
): { label: string; customContent: ReactNode; value: string }[] =>
endpoints.map((x) => {
const spanCodeObjectId = x.endpointInfo.entrySpanCodeObjectId;
const route = trimEndpointScheme(x.endpointInfo.route);
return {
label: route,
customContent: (
<EndpointSelectedOption
serviceName={x.endpointInfo.serviceName}
route={route}
spanCodeObjectId={spanCodeObjectId}
onClick={handleLinkClick}
/>
),
value: spanCodeObjectId
};
});

export const SpaNPlusOneInsightCard = ({
insight,
onAssetLinkClick,
Expand Down Expand Up @@ -86,18 +63,25 @@ export const SpaNPlusOneInsightCard = ({
<Details>
<Description>Affected Endpoints ({endpoints.length})</Description>
<s.SelectContainer>
<Select
<AffectedEndpointsSelector
value={selectedEndpoint?.endpointInfo.entrySpanCodeObjectId}
onChange={(selectedOption) => {
const selected =
endpoints.find(
(x) =>
x.endpointInfo.entrySpanCodeObjectId === selectedOption
x.endpointInfo.entrySpanCodeObjectId ===
selectedOption?.spanCodeObjectId
) ?? null;

setSelectedEndpoint(selected);
}}
options={renderOptions(endpoints, handleSpanLinkClick)}
options={endpoints.map((x) => ({
route: x.endpointInfo.route,
serviceName: x.endpointInfo.serviceName,
spanCodeObjectId: x.endpointInfo.entrySpanCodeObjectId
}))}
insightType={insight.type}
onAssetLinkClick={handleSpanLinkClick}
/>
{isJaegerEnabled && selectedEndpoint && (
<Tooltip title={"Open Trace"}>
Expand Down
Loading