Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[AP 3560] Build UI for build status #76

Merged
merged 28 commits into from
Sep 14, 2023
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
0b11fc0
Merge branch 'main' of https://github.com/chromaui/storybook-visual-t…
weeksling Sep 2, 2023
ec58e3d
One progress bar for all steps and show progress in message
weeksling Sep 5, 2023
aa8606a
Combine message map and steps weights into one array
weeksling Sep 5, 2023
cdb6e93
Add the expandable progress summary section with some parts faked / n…
weeksling Sep 6, 2023
b61d599
Merge branch 'main' into matt/ap-3560-ext-build-ui-for-build-status
ghengeveld Sep 8, 2023
7cd0334
Pull type defs from constants into types.ts and fix eyebrow progress …
ghengeveld Sep 8, 2023
173f03b
Slightly wider tooltip so the text fits
ghengeveld Sep 10, 2023
542ab9c
Continuously update build progress based on historic or estimated ste…
ghengeveld Sep 11, 2023
08e2be7
Ensure expanded menu isn't impeded by parent height constraint
ghengeveld Sep 11, 2023
91348eb
Add long easing to progress bar width to give impression of continuou…
ghengeveld Sep 11, 2023
c7f5c12
Ignore lint error
ghengeveld Sep 11, 2023
555e27b
Remove unnecessary check
ghengeveld Sep 11, 2023
17dae2c
Remove irrelevant stories
ghengeveld Sep 11, 2023
7b4aa21
Remove unused component
ghengeveld Sep 11, 2023
a9f05ff
Fix 'switch to newer snapshot' button
ghengeveld Sep 11, 2023
7e29b49
Roll BuildProgress back into BuildEyebrow and update stories
ghengeveld Sep 13, 2023
7812218
Add stories for BuildProgressLabel
ghengeveld Sep 13, 2023
7afc8a1
Better variable name
ghengeveld Sep 13, 2023
b4aecf4
Use constant
ghengeveld Sep 13, 2023
259e11e
Avoid 'as any'
ghengeveld Sep 13, 2023
93996b5
Extract check and add comment
ghengeveld Sep 13, 2023
b957400
Fix bar padding
ghengeveld Sep 13, 2023
76831c0
Pull runChromaticBuild out to separate file
ghengeveld Sep 13, 2023
50412c5
Flatten code and make sure we don't have a rogue timeout still running
ghengeveld Sep 13, 2023
554aa74
Add first couple of tests, more TODO
ghengeveld Sep 13, 2023
8b0db73
Merge branch 'main' into matt/ap-3560-ext-build-ui-for-build-status
ghengeveld Sep 14, 2023
799811d
Fix story data
ghengeveld Sep 14, 2023
d059219
Unit test the callbacks directly
ghengeveld Sep 14, 2023
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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
"@storybook/design-system": "^7.15.15",
"chromatic": "^7.1.0",
"date-fns": "^2.30.0",
"filesize": "^10.0.12",
"pluralize": "^8.0.0",
"ts-dedent": "^2.2.0",
"urql": "^4.0.3",
Expand Down
13 changes: 2 additions & 11 deletions src/Panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,13 @@ import { useChannel, useStorybookState } from "@storybook/manager-api";
import React, { useCallback } from "react";

import { Sections } from "./components/layout";
import {
ADDON_ID,
GIT_INFO,
GitInfoPayload,
IS_OUTDATED,
PANEL_ID,
RUNNING_BUILD,
RunningBuildPayload,
START_BUILD,
} from "./constants";
import { ADDON_ID, GIT_INFO, IS_OUTDATED, PANEL_ID, RUNNING_BUILD, START_BUILD } from "./constants";
import { Authentication } from "./screens/Authentication/Authentication";
import { LinkedProject } from "./screens/LinkProject/LinkedProject";
import { LinkingProjectFailed } from "./screens/LinkProject/LinkingProjectFailed";
import { LinkProject } from "./screens/LinkProject/LinkProject";
import { VisualTests } from "./screens/VisualTests/VisualTests";
import { UpdateStatusFunction } from "./types";
import { GitInfoPayload, RunningBuildPayload, UpdateStatusFunction } from "./types";
import { useAddonState } from "./useAddonState/manager";
import { client, Provider, useAccessToken } from "./utils/graphQLClient";
import { useProjectId } from "./utils/useProjectId";
Expand Down
25 changes: 10 additions & 15 deletions src/SidebarTop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,8 @@ import pluralize from "pluralize";
import React, { useEffect, useRef } from "react";

import { SidebarTopButton } from "./components/SidebarTopButton";
import {
ADDON_ID,
IS_OUTDATED,
RUNNING_BUILD,
RunningBuildPayload,
START_BUILD,
} from "./constants";
import { ADDON_ID, IS_OUTDATED, RUNNING_BUILD, START_BUILD } from "./constants";
import { RunningBuildPayload } from "./types";
import { useAddonState } from "./useAddonState/manager";
import { useAccessToken } from "./utils/graphQLClient";
import { useProjectId } from "./utils/useProjectId";
Expand All @@ -28,14 +23,14 @@ export const SidebarTop = ({ api }: SidebarTopProps) => {

const [isOutdated] = useAddonState<boolean>(IS_OUTDATED);
const [runningBuild] = useAddonState<RunningBuildPayload>(RUNNING_BUILD);
const isRunning = !!runningBuild && runningBuild.step !== "complete";
const isRunning = !!runningBuild && runningBuild.currentStep !== "complete";

const lastStep = useRef(runningBuild?.step);
const lastStep = useRef(runningBuild?.currentStep);
useEffect(() => {
if (runningBuild?.step === lastStep.current) return;
lastStep.current = runningBuild?.step;
if (runningBuild?.currentStep === lastStep.current) return;
lastStep.current = runningBuild?.currentStep;

if (runningBuild?.step === "initialize") {
if (runningBuild?.currentStep === "initialize") {
addNotification({
id: `${ADDON_ID}/build-initialize`,
content: {
Expand All @@ -51,7 +46,7 @@ export const SidebarTop = ({ api }: SidebarTopProps) => {
setTimeout(() => clearNotification(`${ADDON_ID}/build-initialize`), 10_000);
}

if (runningBuild?.step === "complete") {
if (runningBuild?.currentStep === "complete") {
addNotification({
id: `${ADDON_ID}/build-complete`,
content: {
Expand All @@ -72,7 +67,7 @@ export const SidebarTop = ({ api }: SidebarTopProps) => {
setTimeout(() => clearNotification(`${ADDON_ID}/build-complete`), 10_000);
}

if (runningBuild?.step === "error") {
if (runningBuild?.currentStep === "error") {
addNotification({
id: `${ADDON_ID}/build-error`,
content: {
Expand All @@ -89,7 +84,7 @@ export const SidebarTop = ({ api }: SidebarTopProps) => {
}, [
addNotification,
clearNotification,
runningBuild?.step,
runningBuild?.currentStep,
runningBuild?.errorCount,
runningBuild?.changeCount,
]);
Expand Down
118 changes: 118 additions & 0 deletions src/buildSteps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// eslint-disable-next-line import/no-unresolved
import { TaskName } from "chromatic/node";
import { filesize } from "filesize";

import { KnownStep, RunningBuildPayload, StepProgressPayload } from "./types";

export const isKnownStep = (
taskOrStep: TaskName | RunningBuildPayload["currentStep"]
): taskOrStep is KnownStep => BUILD_STEP_ORDER.includes(taskOrStep as KnownStep);

export const hasProgressEvent = (task: TaskName) => ["upload", "snapshot"].includes(task);

// Note this does not include the "complete" and "error" steps
export const BUILD_STEP_ORDER: KnownStep[] = [
"initialize",
"build",
"upload",
"verify",
"snapshot",
];

export const BUILD_STEP_CONFIG: Record<
RunningBuildPayload["currentStep"],
{
key: RunningBuildPayload["currentStep"];
emoji: string;
renderName: () => string;
renderProgress: (payload: RunningBuildPayload) => string;
renderComplete: () => string;
estimateDuration: number;
}
> = {
initialize: {
key: "initialize",
emoji: "🚀",
renderName: () => `Initialize build`,
renderProgress: () => `Initializing build`,
renderComplete: () => `Initialized`,
estimateDuration: 2000,
},
build: {
key: "build",
emoji: "🏗",
renderName: () => `Build Storybook`,
renderProgress: () => `Building your Storybook...`,
renderComplete: () => `Storybook built`,
estimateDuration: 30_000,
},
upload: {
key: "upload",
emoji: "📡",
renderName: () => `Publish your Storybook`,
renderProgress: ({ stepProgress }) => {
const { numerator, denominator } = stepProgress.upload;
if (!denominator) return `Uploading files`;
const { value: total, exponent } = filesize(denominator, {
output: "object",
round: 1,
});
const { value: progress, symbol } = filesize(numerator, {
exponent,
output: "object",
round: 1,
});
return `Uploading files (${progress}/${total} ${symbol})`;
},
renderComplete: () => `Publish complete`,
estimateDuration: 30_000,
},
verify: {
key: "verify",
emoji: "🔍",
renderName: () => `Verify your Storybook`,
renderProgress: () => `Verifying contents...`,
renderComplete: () => `Storybook verified`,
estimateDuration: 10_000,
},
snapshot: {
key: "snapshot",
emoji: "📸",
renderName: () => `Run visual tests`,
renderProgress: ({ stepProgress }) => {
const { numerator, denominator } = stepProgress.snapshot;
return denominator
? `Running visual tests (${numerator}/${denominator})`
: `Running visual tests`;
},
renderComplete: () => `Tested your stories`,
estimateDuration: 60_000,
},

// These are special steps that are not part of the build process
complete: {
key: "complete",
emoji: "🎉",
renderName: () => `Visual tests completed!`,
renderProgress: () => `Visual tests completed!`,
renderComplete: () => `Visual tests completed!`,
estimateDuration: 0,
},
error: {
key: "error",
emoji: "🚨",
renderName: () => `Build failed`,
renderProgress: () => `Build failed`,
renderComplete: () => `Build failed`,
estimateDuration: 0,
},
};

export const INITIAL_BUILD_PAYLOAD = {
buildProgressPercentage: 0,
currentStep: BUILD_STEP_ORDER[0],
stepProgress: Object.fromEntries(BUILD_STEP_ORDER.map((step) => [step, {}])) as Record<
KnownStep,
StepProgressPayload
>,
};
102 changes: 102 additions & 0 deletions src/components/BuildProgressLabel.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import type { Meta, StoryObj } from "@storybook/react";

import { INITIAL_BUILD_PAYLOAD } from "../buildSteps";
import { withFigmaDesign } from "../utils/withFigmaDesign";
import { BuildProgressLabel } from "./BuildProgressLabel";

const meta = {
component: BuildProgressLabel,
} satisfies Meta<typeof BuildProgressLabel>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Initialize: Story = {
args: {
runningBuild: INITIAL_BUILD_PAYLOAD,
},
parameters: withFigmaDesign(
"https://www.figma.com/file/GFEbCgCVDtbZhngULbw2gP/Visual-testing-in-Storybook?type=design&node-id=2892-73423&mode=design&t=gIM40WT0324ynPQD-4"
),
};

export const Build: Story = {
args: {
runningBuild: {
...INITIAL_BUILD_PAYLOAD,
buildProgressPercentage: 8,
currentStep: "build",
},
},
parameters: withFigmaDesign(
"https://www.figma.com/file/GFEbCgCVDtbZhngULbw2gP/Visual-testing-in-Storybook?type=design&node-id=2892-73453&mode=design&t=gIM40WT0324ynPQD-4"
),
};

export const Upload: Story = {
args: {
runningBuild: {
...INITIAL_BUILD_PAYLOAD,
buildProgressPercentage: 50,
currentStep: "upload",
stepProgress: {
...INITIAL_BUILD_PAYLOAD.stepProgress,
upload: {
startedAt: Date.now() - 3000,
numerator: 4_200_000,
denominator: 123_000_000,
},
},
},
},
parameters: withFigmaDesign(
"https://www.figma.com/file/GFEbCgCVDtbZhngULbw2gP/Visual-testing-in-Storybook?type=design&node-id=2935-71430&mode=design&t=gIM40WT0324ynPQD-4"
),
};

export const Verify: Story = {
args: {
runningBuild: {
...INITIAL_BUILD_PAYLOAD,
buildProgressPercentage: 75,
currentStep: "verify",
},
},
parameters: withFigmaDesign(
"https://www.figma.com/file/GFEbCgCVDtbZhngULbw2gP/Visual-testing-in-Storybook?type=design&node-id=2935-72020&mode=design&t=gIM40WT0324ynPQD-4"
),
};

export const Snapshot: Story = {
args: {
runningBuild: {
...INITIAL_BUILD_PAYLOAD,
buildProgressPercentage: 90,
currentStep: "snapshot",
stepProgress: {
...INITIAL_BUILD_PAYLOAD.stepProgress,
snapshot: {
startedAt: Date.now() - 5000,
numerator: 25,
denominator: 50,
},
},
},
},
parameters: withFigmaDesign(
"https://www.figma.com/file/GFEbCgCVDtbZhngULbw2gP/Visual-testing-in-Storybook?type=design&node-id=2892-74603&mode=design&t=gIM40WT0324ynPQD-4"
),
};

export const Complete: Story = {
args: {
runningBuild: {
...INITIAL_BUILD_PAYLOAD,
currentStep: "complete",
buildProgressPercentage: 100,
},
},
parameters: withFigmaDesign(
"https://www.figma.com/file/GFEbCgCVDtbZhngULbw2gP/Visual-testing-in-Storybook?type=design&node-id=2892-74801&mode=design&t=gIM40WT0324ynPQD-4"
),
};
24 changes: 10 additions & 14 deletions src/components/BuildProgressLabel.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
import React from "react";

import { RunningBuildPayload } from "../constants";

const messageMap: Record<RunningBuildPayload["step"], (payload: RunningBuildPayload) => string> = {
initialize: () => `📦 Validating Storybook files...`,
build: () => `📦 Validating Storybook files...`,
upload: () => `📡 Uploading to Chromatic...`, // TODO represent progress in bytes
verify: () => `🛠️ Initiating build...`, // TODO build number
snapshot: () => `👀 Running visual tests...`, // TODO count
complete: () => `🎉 Visual tests completed!`,
error: () => `❌ Build failed`, // TODO error
};
import { BUILD_STEP_CONFIG } from "../buildSteps";
import { RunningBuildPayload } from "../types";

interface BuildProgressLabelProps {
runningBuild: RunningBuildPayload;
}

export const BuildProgressLabel = ({ runningBuild }: BuildProgressLabelProps) => (
<>{messageMap[runningBuild.step](runningBuild)}</>
);
export const BuildProgressLabel = ({ runningBuild }: BuildProgressLabelProps) => {
const { emoji, renderProgress } = BUILD_STEP_CONFIG[runningBuild.currentStep];
return (
<>
{emoji} {renderProgress(runningBuild)}
</>
);
};
ghengeveld marked this conversation as resolved.
Show resolved Hide resolved
29 changes: 0 additions & 29 deletions src/components/RunTestsButton.stories.tsx

This file was deleted.

Loading