Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
444d241
Refactor empty state card for Highlights
kshmidt-digma Apr 26, 2024
dd1b08b
Add Highlights Scaling widget
kshmidt-digma Apr 26, 2024
e32e639
Bump version
kshmidt-digma Apr 26, 2024
db0bcfb
Bump version
kshmidt-digma Apr 26, 2024
8cd6a3c
Update stories, add feature flag
kshmidt-digma Apr 26, 2024
2186012
Fix empty state cards
kshmidt-digma Apr 26, 2024
3ebfb5f
Merge branch 'fix/highlights' into feature/highlights-scaling
kshmidt-digma Apr 26, 2024
be9a4e5
Add Highlights Tests widget
kshmidt-digma Apr 26, 2024
3344738
Fix feature flag
kshmidt-digma Apr 26, 2024
c802df2
Update icon
kshmidt-digma Apr 26, 2024
9379ee3
Use route constant
kshmidt-digma Apr 26, 2024
94dc0c2
Merge branch 'fix/highlights' into feature/highlights-scaling
kshmidt-digma Apr 26, 2024
679bb14
Merge branch 'feature/highlights-scaling' into feature/highlights-tests
kshmidt-digma Apr 26, 2024
835608c
Rename field
kshmidt-digma Apr 29, 2024
5dfa456
Merge branch 'fix/highlights' into feature/highlights-scaling
kshmidt-digma Apr 29, 2024
b968473
Merge branch 'feature/highlights-scaling' into feature/highlights-tests
kshmidt-digma Apr 29, 2024
3e6e8a1
Merge branch 'main' into feature/highlights-scaling
kshmidt-digma Apr 30, 2024
a75cd8c
Add documentation links
kshmidt-digma Apr 30, 2024
780397d
Merge branch 'feature/highlights-scaling' into feature/highlights-tests
kshmidt-digma Apr 30, 2024
be6e6d1
Update Learn more link
kshmidt-digma Apr 30, 2024
4abd982
Switch to using of isCentralize flag
kshmidt-digma Apr 30, 2024
97637b9
Merge branch 'feature/highlights-scaling' into feature/highlights-tests
kshmidt-digma Apr 30, 2024
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
4 changes: 1 addition & 3 deletions src/components/Assets/AssetList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import { isEnvironment } from "../../../typeGuards/isEnvironment";
import { isNumber } from "../../../typeGuards/isNumber";
import { isString } from "../../../typeGuards/isString";
import { ConfigContext } from "../../common/App/ConfigContext";
import { DeploymentType } from "../../common/App/types";
import { EmptyState } from "../../common/EmptyState";
import { Menu } from "../../common/Menu";
import { NewCircleLoader } from "../../common/NewCircleLoader";
Expand Down Expand Up @@ -227,8 +226,7 @@ export const AssetList = (props: AssetListProps) => {
const isImpactHidden = useMemo(
() =>
!(
config.backendInfo?.deploymentType === DeploymentType.HELM &&
config.environment?.type === "Public"
config.backendInfo?.centralize && config.environment?.type === "Public"
),
[config.backendInfo?.deploymentType, config.environment?.type]
);
Expand Down
51 changes: 51 additions & 0 deletions src/components/Highlights/Highlights.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { Meta, StoryObj } from "@storybook/react";

import { Highlights } from ".";
import { featureFlagMinBackendVersions } from "../../featureFlags";
import { FeatureFlag } from "../../types";
import { actions as mainActions } from "../Main/actions";
import { ConfigContext, initialState } from "../common/App/ConfigContext";
import { DeploymentType } from "../common/App/types";
import { mockedImpactData } from "./Impact/mockData";
import { mockedPerformanceData } from "./Performance/mockData";
import { mockedScalingData } from "./Scaling/mockData";
import { mockedTestsData } from "./Tests/mockData";
import { mockedTopIssuesData } from "./TopIssues/mockData";

// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction
Expand All @@ -21,7 +27,25 @@ export default meta;
type Story = StoryObj<typeof meta>;

// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args

const mockedConfig = {
...initialState,
backendInfo: {
applicationVersion:
featureFlagMinBackendVersions[FeatureFlag.ARE_TESTS_HIGHLIGHTS_ENABLED],
deploymentType: DeploymentType.HELM,
centralize: true
}
};

export const Default: Story = {
decorators: [
(Story) => (
<ConfigContext.Provider value={mockedConfig}>
<Story />
</ConfigContext.Provider>
)
],
play: () => {
window.setTimeout(() => {
window.postMessage({
Expand All @@ -39,11 +63,28 @@ export const Default: Story = {
action: mainActions.SET_HIGHLIGHTS_IMPACT_DATA,
payload: mockedImpactData
});
window.postMessage({
type: "digma",
action: mainActions.SET_HIGHLIGHTS_SCALING_DATA,
payload: mockedScalingData
});
window.postMessage({
type: "digma",
action: mainActions.SET_HIGHLIGHTS_TESTS_DATA,
payload: mockedTestsData
});
}, 1000);
}
};

export const Empty: Story = {
decorators: [
(Story) => (
<ConfigContext.Provider value={mockedConfig}>
<Story />
</ConfigContext.Provider>
)
],
play: () => {
window.setTimeout(() => {
window.postMessage({
Expand All @@ -61,6 +102,16 @@ export const Empty: Story = {
action: mainActions.SET_HIGHLIGHTS_IMPACT_DATA,
payload: { impactHighlights: [] }
});
window.postMessage({
type: "digma",
action: mainActions.SET_HIGHLIGHTS_SCALING_DATA,
payload: { scaling: [] }
});
window.postMessage({
type: "digma",
action: mainActions.SET_HIGHLIGHTS_TESTS_DATA,
payload: { tests: { totalCount: 0, failedCount: 0 } }
});
}, 1000);
}
};
101 changes: 101 additions & 0 deletions src/components/Highlights/Scaling/Scaling.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { Meta, StoryObj } from "@storybook/react";

import { Scaling } from ".";
import { featureFlagMinBackendVersions } from "../../../featureFlags";
import { FeatureFlag } from "../../../types";
import { actions } from "../../Main/actions";
import { ConfigContext, initialState } from "../../common/App/ConfigContext";
import { DeploymentType } from "../../common/App/types";
import { mockedScalingData } from "./mockData";

// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction
const meta: Meta<typeof Scaling> = {
title: "Highlights/Scaling",
component: Scaling,
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>;

// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args

const mockedConfig = {
...initialState,
backendInfo: {
applicationVersion:
featureFlagMinBackendVersions[FeatureFlag.ARE_IMPACT_HIGHLIGHTS_ENABLED],
deploymentType: DeploymentType.HELM,
centralize: true
}
};

export const Default: Story = {
decorators: [
(Story) => (
<ConfigContext.Provider value={mockedConfig}>
<Story />
</ConfigContext.Provider>
)
],
play: () => {
window.setTimeout(() => {
window.postMessage({
type: "digma",
action: actions.SET_HIGHLIGHTS_SCALING_DATA,
payload: mockedScalingData
});
});
}
};

export const Loading: Story = {
decorators: [
(Story) => (
<ConfigContext.Provider value={mockedConfig}>
<Story />
</ConfigContext.Provider>
)
]
};

export const Empty: Story = {
decorators: [
(Story) => (
<ConfigContext.Provider value={mockedConfig}>
<Story />
</ConfigContext.Provider>
)
],
play: () => {
window.setTimeout(() => {
window.postMessage({
type: "digma",
action: actions.SET_HIGHLIGHTS_SCALING_DATA,
payload: { scaling: [] }
});
});
}
};

export const Disabled: Story = {
decorators: [
(Story) => (
<ConfigContext.Provider
value={{
...mockedConfig,
backendInfo: {
...mockedConfig.backendInfo,
centralize: false
}
}}
>
<Story />
</ConfigContext.Provider>
)
]
};
175 changes: 175 additions & 0 deletions src/components/Highlights/Scaling/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import { Row, createColumnHelper } from "@tanstack/react-table";
import { useContext, useEffect, useState } from "react";
import { actions as globalActions } from "../../../actions";
import { ROUTES, SCALING_ISSUE_DOCUMENTATION_URL } from "../../../constants";
import { usePrevious } from "../../../hooks/usePrevious";
import { ChangeViewPayload } from "../../../types";
import { openURLInDefaultBrowser } from "../../../utils/actions/openURLInDefaultBrowser";
import { sendUserActionTrackingEvent } from "../../../utils/actions/sendUserActionTrackingEvent";
import { getDurationString } from "../../../utils/getDurationString";
import { ConfigContext } from "../../common/App/ConfigContext";
import { CrossCircleIcon } from "../../common/icons/16px/CrossCircleIcon";
import { RefreshIcon } from "../../common/icons/16px/RefreshIcon";
import { CheckCircleIcon } from "../../common/icons/20px/CheckCircleIcon";
import { Button } from "../../common/v3/Button";
import { Card } from "../../common/v3/Card";
import { EmptyStateCard } from "../EmptyStateCard";
import { addEnvironmentColumns } from "../TopIssues/highlightCards/addEnvironmentColumns";
import { EnvironmentData } from "../TopIssues/types";
import { Section } from "../common/Section";
import { Table } from "../common/Table";
import { TableText } from "../common/TableText";
import { handleEnvironmentTableRowClick } from "../handleEnvironmentTableRowClick";
import { trackingEvents } from "../tracking";
import * as s from "./styles";
import { EnvironmentScalingData } from "./types";
import { useScalingData } from "./useScalingData";

export const Scaling = () => {
const [isInitialLoading, setIsInitialLoading] = useState(true);
const { data, getData } = useScalingData();
const previousData = usePrevious(data);
const config = useContext(ConfigContext);

useEffect(() => {
getData();
}, []);

useEffect(() => {
if (!previousData && data) {
setIsInitialLoading(false);
}
}, [previousData, data]);

const renderScalingCard = (
data: EnvironmentData<EnvironmentScalingData>[]
) => {
const columnHelper =
createColumnHelper<EnvironmentData<EnvironmentScalingData>>();

const metricsColumns = [
columnHelper.accessor((x) => x.metrics.concurrency, {
header: "Concurrency",
cell: (info) => {
const value = info.getValue();
const concurrencyString = String(value);

return (
<TableText title={concurrencyString}>{concurrencyString}</TableText>
);
}
}),
columnHelper.accessor((x) => x.metrics.duration, {
header: "Duration",
cell: (info) => {
const value = info.getValue();
const durationString = getDurationString(value);

return <TableText title={durationString}>{durationString}</TableText>;
}
})
];

const columns = addEnvironmentColumns(columnHelper, metricsColumns);

const handleTableRowClick = (
row: Row<EnvironmentData<EnvironmentScalingData>>
) => {
sendUserActionTrackingEvent(
trackingEvents.SCALING_CARD_TABLE_ROW_CLICKED
);
handleEnvironmentTableRowClick(
config.environments,
row.original.environmentId,
ROUTES.INSIGHTS
);
};

return (
<Card
header={<s.CardTitle>Scaling badly</s.CardTitle>}
content={
<Table<EnvironmentData<EnvironmentScalingData>>
columns={columns}
data={data}
onRowClick={handleTableRowClick}
/>
}
/>
);
};

const renderCard = () => {
const handleLearnMoreButtonClick = () => {
sendUserActionTrackingEvent(
trackingEvents.SCALING_CARD_LEARN_MORE_BUTTON_CLICKED
);

openURLInDefaultBrowser(SCALING_ISSUE_DOCUMENTATION_URL);
};

const handleViewAnalyticsButtonClick = () => {
sendUserActionTrackingEvent(
trackingEvents.SCALING_CARD_VIEW_ANALYTICS_BUTTON_CLICKED
);

window.sendMessageToDigma<ChangeViewPayload>({
action: globalActions.CHANGE_VIEW,
payload: {
view: ROUTES.ANALYTICS
}
});
};

if (!config.backendInfo?.centralize) {
return (
<EmptyStateCard
icon={CrossCircleIcon}
title={"Unlock Scaling Issues"}
text={"Connect a CI/Prod environment to run code at scale"}
customContent={
<Button
buttonType={"secondary"}
onClick={handleLearnMoreButtonClick}
label={"Learn more"}
/>
}
/>
);
}

if (isInitialLoading) {
return (
<EmptyStateCard
type={"lowSeverity"}
icon={RefreshIcon}
title={"Waiting for data"}
/>
);
}

// TODO: show the card for partial data

if (!data || data.scaling.length === 0) {
return (
<EmptyStateCard
type={"success"}
icon={CheckCircleIcon}
title={"No scaling issue found"}
text={"Looks like this asset is scaling well"}
customContent={
<Button
buttonType={"secondary"}
onClick={handleViewAnalyticsButtonClick}
label={"View analytics"}
/>
}
/>
);
}

return renderScalingCard(data.scaling);
};

return <Section title={"Scaling"}>{renderCard()}</Section>;
};
Loading