Skip to content

Commit

Permalink
DEVPROD-10195: Add task stats tooltip to waterfall (#480)
Browse files Browse the repository at this point in the history
  • Loading branch information
sophstad authored Nov 6, 2024
1 parent a76e168 commit 157582e
Show file tree
Hide file tree
Showing 17 changed files with 425 additions and 137 deletions.
10 changes: 10 additions & 0 deletions apps/spruce/cypress/integration/waterfall/waterfall.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,16 @@ describe("waterfall page", () => {
});
});

describe("task stats tooltip", () => {
it("shows task stats when clicked", () => {
cy.dataCy("task-stats-tooltip").should("not.exist");
cy.dataCy("task-stats-tooltip-button").eq(3).click();
cy.dataCy("task-stats-tooltip").should("be.visible");
cy.dataCy("task-stats-tooltip").should("contain.text", "Failed");
cy.dataCy("task-stats-tooltip").should("contain.text", "Succeeded");
});
});

describe("pinned build variants", () => {
beforeEach(() => {
cy.visit("/project/evergreen/waterfall");
Expand Down
6 changes: 6 additions & 0 deletions apps/spruce/src/gql/fragments/waterfall.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,11 @@ fragment WaterfallVersion on Version {
order
requester
revision
taskStatusStats(options: {}) {
counts {
count
status
}
}
...UpstreamProject
}
17 changes: 17 additions & 0 deletions apps/spruce/src/gql/generated/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3507,6 +3507,7 @@ export type WaterfallPagination = {
export type WaterfallTask = {
__typename?: "WaterfallTask";
displayName: Scalars["String"]["output"];
displayStatus: Scalars["String"]["output"];
execution: Scalars["Int"]["output"];
id: Scalars["String"]["output"];
status: Scalars["String"]["output"];
Expand Down Expand Up @@ -4950,6 +4951,14 @@ export type WaterfallVersionFragment = {
requester: string;
revision: string;
gitTags?: Array<{ __typename?: "GitTag"; tag: string }> | null;
taskStatusStats?: {
__typename?: "TaskStats";
counts?: Array<{
__typename?: "StatusCount";
count: number;
status: string;
}> | null;
} | null;
upstreamProject?: {
__typename?: "UpstreamProject";
owner: string;
Expand Down Expand Up @@ -9642,6 +9651,14 @@ export type WaterfallQuery = {
requester: string;
revision: string;
gitTags?: Array<{ __typename?: "GitTag"; tag: string }> | null;
taskStatusStats?: {
__typename?: "TaskStats";
counts?: Array<{
__typename?: "StatusCount";
count: number;
status: string;
}> | null;
} | null;
upstreamProject?: {
__typename?: "UpstreamProject";
owner: string;
Expand Down
27 changes: 10 additions & 17 deletions apps/spruce/src/pages/waterfall/BuildRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,21 @@ import Icon from "components/Icon";
import { StyledLink } from "components/styles";
import { getTaskRoute, getVariantHistoryRoute } from "constants/routes";
import { size } from "constants/tokens";
import { WaterfallBuild, WaterfallBuildVariant } from "gql/generated/types";
import { statusColorMap, statusIconMap } from "./icons";
import {
BuildVariantTitle,
columnBasis,
gridGroupCss,
InactiveVersion,
Row,
SQUARE_SIZE,
taskStatusStyleMap,
} from "./styles";
import { WaterfallVersion } from "./types";
import { Build, BuildVariant, WaterfallVersion } from "./types";

const { black, gray, white } = palette;

type Props = {
build: WaterfallBuildVariant;
build: BuildVariant;
handlePinClick: () => void;
pinned: boolean;
projectIdentifier: string;
Expand Down Expand Up @@ -96,18 +96,18 @@ export const BuildRow: React.FC<Props> = ({
/>
);
}
return <Build key={version?.id} />;
return <BuildContainer key={version?.id} />;
})}
</BuildGroup>
</Row>
);
};

const BuildGrid: React.FC<{
build: WaterfallBuild;
build: Build;
handleTaskClick: (s: string) => () => void;
}> = ({ build, handleTaskClick }) => (
<Build
<BuildContainer
onClick={(event: React.MouseEvent) => {
handleTaskClick(
(event.target as HTMLDivElement)?.getAttribute("status") ?? "",
Expand All @@ -128,7 +128,7 @@ const BuildGrid: React.FC<{
/>
);
})}
</Build>
</BuildContainer>
);

const BuildGroup = styled.div`
Expand All @@ -139,7 +139,7 @@ const BuildGroup = styled.div`
padding-top: ${size.xs};
`;

const Build = styled.div`
const BuildContainer = styled.div`
${columnBasis}
`;

Expand All @@ -148,8 +148,6 @@ const StyledIconButton = styled(IconButton)`
${({ active }) => active && "transform: rotate(-30deg);"}
`;

const SQUARE_SIZE = 16;

const Square = styled(Link)<{ status: TaskStatus }>`
width: ${SQUARE_SIZE}px;
height: ${SQUARE_SIZE}px;
Expand All @@ -159,12 +157,7 @@ const Square = styled(Link)<{ status: TaskStatus }>`
cursor: pointer;
position: relative;
${({ status }) => {
const icon = statusIconMap?.[status];
const iconStyle = icon ? `background-image: ${icon};` : "";
return `${iconStyle}
background-color: ${statusColorMap[status]};`;
}}
${({ status }) => taskStatusStyleMap[status]}
/* Tooltip */
:before {
Expand Down
90 changes: 90 additions & 0 deletions apps/spruce/src/pages/waterfall/TaskStatsTooltip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { useRef, useState } from "react";
import styled from "@emotion/styled";
import IconButton from "@leafygreen-ui/icon-button";
import Popover, { Align } from "@leafygreen-ui/popover";
import { taskStatusToCopy } from "@evg-ui/lib/constants/task";
import { TaskStatus } from "@evg-ui/lib/types/task";
import Icon from "components/Icon";
import { Divider } from "components/styles";
import { PopoverContainer } from "components/styles/Popover";
import { size } from "constants/tokens";
import { WaterfallVersionFragment } from "gql/generated/types";
import { useOnClickOutside } from "hooks";
import { SQUARE_SIZE, taskStatusStyleMap } from "./styles";

export const TaskStatsTooltip: React.FC<
Pick<WaterfallVersionFragment, "taskStatusStats">
> = ({ taskStatusStats }) => {
const [open, setOpen] = useState(false);

const buttonRef = useRef<HTMLButtonElement>(null);
const popoverRef = useRef<HTMLDivElement>(null);

useOnClickOutside([buttonRef, popoverRef], () => setOpen(false));

const totalTaskCount =
taskStatusStats?.counts?.reduce((total, { count }) => total + count, 0) ??
0;

return (
<>
<BtnContainer>
<IconButton
ref={buttonRef}
aria-label="Show task stats"
data-cy="task-stats-tooltip-button"
onClick={() => setOpen((o) => !o)}
>
<Icon glyph="Charts" />
</IconButton>
</BtnContainer>
<Popover ref={popoverRef} active={open} align={Align.Right}>
<PopoverContainer data-cy="task-stats-tooltip">
<Table>
{taskStatusStats?.counts?.map(({ count, status }) => (
<Row>
<Count>{count}</Count>
<Cell>
<Square status={status as TaskStatus} />
</Cell>
<Cell>{taskStatusToCopy[status as TaskStatus]}</Cell>
</Row>
))}
<Row>
<Cell colSpan={3}>
<Divider />
</Cell>
</Row>
<Row>
<Count>{totalTaskCount}</Count>
<Cell colSpan={2}>Total tasks</Cell>
</Row>
</Table>
</PopoverContainer>
</Popover>
</>
);
};

const BtnContainer = styled.div`
display: inline-block;
`;

const Table = styled.table``;

const Row = styled.tr``;

const Cell = styled.td`
padding: 0 ${size.xxs};
`;

const Count = styled(Cell)`
font-feature-settings: "tnum";
text-align: right;
`;

const Square = styled.div<{ status: TaskStatus }>`
${({ status }) => taskStatusStyleMap[status]}
height: ${SQUARE_SIZE}px;
width: ${SQUARE_SIZE}px;
`;
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,46 @@ export const Broken: StoryObj<typeof VersionLabel> = {
args: versionBroken,
};

export const TaskStatsTooltip: StoryObj<typeof VersionLabel> = {
...Default,
args: {
...version,
taskStatusStats: {
counts: [
{
status: "blocked",
count: 4,
},
{
status: "failed",
count: 3,
},
{
status: "setup-failed",
count: 3,
},
{
status: "started",
count: 22,
},
{
status: "success",
count: 255,
},
{
status: "unscheduled",
count: 2313,
},
{
status: "will-run",
count: 100,
},
],
},
view: VersionLabelView.Waterfall,
},
};

const Container = styled.div`
max-width: 300px;
`;
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,35 @@
class="css-1pp2qz6"
>
<div
class="css-bnbttx-VersionContainer-columnBasis-wordBreakCss e3zirl02"
class="css-bnbttx-VersionContainer-columnBasis-wordBreakCss e3zirl03"
data-cy="version-label-active"
>
<p
class="leafygreen-ui-1tb6tuo"
<div
class="css-1idzah1-HeaderLine e3zirl00"
>
<a
class="lg-ui-0000 leafygreen-ui-hvznge"
href="/version/evergreen_ui_aec8832bace91f0f3b6d8ad3bb3b27fb4263be83/tasks"
<p
class="leafygreen-ui-1tb6tuo"
>
<code
class="leafygreen-ui-i6ovbf"
<a
class="lg-ui-0000 leafygreen-ui-hvznge"
href="/version/evergreen_ui_aec8832bace91f0f3b6d8ad3bb3b27fb4263be83/tasks"
>
aec8832
</code>
</a>

09/19/2024, 10:56
<div
class="css-etc2w8-StyledBadge e3zirl00 leafygreen-ui-dhdzha"
>
Broken
</div>
</p>
<code
class="leafygreen-ui-i6ovbf"
>
aec8832
</code>
</a>
09/19/2024, 10:56
<div
class="css-etc2w8-StyledBadge e3zirl01 leafygreen-ui-dhdzha"
>
Broken
</div>
</p>
</div>
<p
class="css-9ip3is-CommitMessage e3zirl01 leafygreen-ui-1tb6tuo"
class="css-9ip3is-CommitMessage e3zirl02 leafygreen-ui-1tb6tuo"
view="modal"
>
<strong>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,30 @@
class="css-1pp2qz6"
>
<div
class="css-bnbttx-VersionContainer-columnBasis-wordBreakCss e3zirl02"
class="css-bnbttx-VersionContainer-columnBasis-wordBreakCss e3zirl03"
data-cy="version-label-active"
>
<p
class="leafygreen-ui-1tb6tuo"
<div
class="css-1idzah1-HeaderLine e3zirl00"
>
<a
class="lg-ui-0000 leafygreen-ui-hvznge"
href="/version/evergreen_ui_aec8832bace91f0f3b6d8ad3bb3b27fb4263be83/tasks"
<p
class="leafygreen-ui-1tb6tuo"
>
<code
class="leafygreen-ui-i6ovbf"
<a
class="lg-ui-0000 leafygreen-ui-hvznge"
href="/version/evergreen_ui_aec8832bace91f0f3b6d8ad3bb3b27fb4263be83/tasks"
>
aec8832
</code>
</a>

09/19/2024, 10:56
</p>
<code
class="leafygreen-ui-i6ovbf"
>
aec8832
</code>
</a>
09/19/2024, 10:56
</p>
</div>
<p
class="css-9ip3is-CommitMessage e3zirl01 leafygreen-ui-1tb6tuo"
class="css-9ip3is-CommitMessage e3zirl02 leafygreen-ui-1tb6tuo"
view="modal"
>
<strong>
Expand Down
Loading

0 comments on commit 157582e

Please sign in to comment.