From d439a0398b2caaa6e34dd96a620c58f84e77f37a Mon Sep 17 00:00:00 2001 From: Matthew Weeks Date: Mon, 26 Jun 2023 14:23:48 -0400 Subject: [PATCH 01/28] add back prerelease check so we have somewhere to add additional checks down the line, matches other addons --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 1caf85ab..14a6ec51 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "lint": "eslint src --max-warnings 0 --report-unused-disable-directives", "release": "pnpm run build && auto shipit", "start": "run-p build:watch 'storybook --quiet'", + "prerelease": "zx scripts/prepublish-checks.mjs", "storybook": "storybook dev -p 6006" }, "dependencies": { From 1973bd029f3c40490c4e60cae886691e52eb00b7 Mon Sep 17 00:00:00 2001 From: Matthew Weeks Date: Tue, 27 Jun 2023 12:05:11 -0400 Subject: [PATCH 02/28] WIP: Adding the useParameter hook to get the projectId and token (currently failing for multiple reasons =_=) --- .storybook/main.ts | 1 + src/Panel.tsx | 7 ++++++ src/constants.ts | 2 ++ src/gql/graphql.ts | 30 +++++++++++++++++++++++ src/screens/VisualTests/VisualTests.tsx | 3 +++ src/utils/useProjectId.ts | 32 +++++++++++++++++++++++++ 6 files changed, 75 insertions(+) create mode 100644 src/utils/useProjectId.ts diff --git a/.storybook/main.ts b/.storybook/main.ts index ca904d3b..9045d12b 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -17,6 +17,7 @@ const config: StorybookConfig = { name: "../dist/index.js", options: { projectToken: "chpt_c4206d1157d8947", + projectId: "6480e1b0042842f149cfd74c", // Default to the the production project of this addon - WILL BE MOVED TO preview.tsx since we can't access options in the manager. }, }, ], diff --git a/src/Panel.tsx b/src/Panel.tsx index c125bf6a..735b6864 100644 --- a/src/Panel.tsx +++ b/src/Panel.tsx @@ -3,9 +3,11 @@ import React, { useCallback } from "react"; import { ADDON_ID, PANEL_ID, START_BUILD } from "./constants"; import { Authentication } from "./screens/Authentication/Authentication"; +import { LinkProject } from "./screens/LinkProject/LinkProject"; import { VisualTests } from "./screens/VisualTests/VisualTests"; import { AddonState } from "./types"; import { client, Provider, useAccessToken } from "./utils/graphQLClient"; +import { useProjectId } from "./utils/useProjectId"; interface PanelProps { active: boolean; @@ -13,6 +15,7 @@ interface PanelProps { export const Panel = ({ active }: PanelProps) => { const [accessToken, setAccessToken] = useAccessToken(); + const [state, setAddonState] = useAddonState(ADDON_ID, { isOutdated: true }); const { storyId } = useStorybookState(); @@ -33,6 +36,8 @@ export const Panel = ({ active }: PanelProps) => { emit(START_BUILD); }, [emit, state, setAddonState]); + const [projectId] = useProjectId(); + // Render a hidden element when the addon panel is not active. // Storybook's AddonPanel component does the same but it's not styleable so we don't use it. if (!active) return )} - + ); } -// TODO: This should just be controlled by parent component and set using projectId && projectIdChanged from useProjectId() export const LinkedProject = ({ projectId, goToNext, @@ -154,8 +149,8 @@ export const LinkedProject = ({ Project linked!

- We added project ID to main.js. The design-system app ID will be used to reference - prior tests. Please commit this change to continue using this addon. + We added project ID to main.js. The {data.project.name} app ID will be used to + reference prior tests. Please commit this change to continue using this addon.

-

- What is the app ID for? Learn More » -

- {data?.project && ( -
- Selected project - Baselines will be used with this project. - - {data.project.name} - -
- )} - {data.project.lastBuild && ( -

- Last build: {data.project.lastBuild.number} on branch{" "} - {data.project.lastBuild.branch} -

- )} - - )} - ; - - - ); -}; diff --git a/src/screens/LinkProject/LinkedProject.tsx b/src/screens/LinkProject/LinkedProject.tsx new file mode 100644 index 00000000..22a8962d --- /dev/null +++ b/src/screens/LinkProject/LinkedProject.tsx @@ -0,0 +1,79 @@ +import { Icon } from "@storybook/design-system"; +import React from "react"; +import { useQuery } from "urql"; + +import { Button } from "../../components/Button"; +import { Container } from "../../components/Container"; +import { Heading } from "../../components/Heading"; +import { Stack } from "../../components/Stack"; +import { Text } from "../../components/Text"; +import { graphql } from "../../gql"; +import { ProjectQueryQuery } from "../../gql/graphql"; + +const ProjectQuery = graphql(/* GraphQL */ ` + query ProjectQuery($projectId: ID!) { + project(id: $projectId) { + id + name + webUrl + lastBuild { + branch + number + } + } + } +`); + +export const LinkedProject = ({ + projectId, + goToNext, +}: { + projectId: string; + goToNext: () => void; +}) => { + const [{ data, fetching, error }] = useQuery({ + query: ProjectQuery, + variables: { projectId }, + }); + + return ( + + + {fetching &&

Loading...

} + {error &&

{error.message}

} + {data?.project && ( + + + Project linked! +

+ We added project ID to main.js. The {data.project.name} app ID will be used to + reference prior tests. Please commit this change to continue using this addon. +

+ +

+ What is the app ID for? Learn More » +

+ {data?.project && ( +
+ Selected project + Baselines will be used with this project. + + {data.project.name} + +
+ )} + {data.project.lastBuild && ( +

+ Last build: {data.project.lastBuild.number} on branch{" "} + {data.project.lastBuild.branch} +

+ )} +
+ )} + ; +
+
+ ); +}; From fb359d28852b7ffbb829128270135ff9b44cfa24 Mon Sep 17 00:00:00 2001 From: Matthew Weeks Date: Tue, 11 Jul 2023 17:58:58 -0400 Subject: [PATCH 15/28] Fix visual test queries (useProjectId only in panel, no parameters hooks in stories allowed) --- src/Panel.tsx | 1 + src/gql/gql.ts | 4 +- src/gql/graphql.ts | 4 +- .../VisualTests/VisualTests.stories.tsx | 9 +++++ src/screens/VisualTests/VisualTests.tsx | 37 +++++++++---------- 5 files changed, 31 insertions(+), 24 deletions(-) diff --git a/src/Panel.tsx b/src/Panel.tsx index e35894a9..4524be9c 100644 --- a/src/Panel.tsx +++ b/src/Panel.tsx @@ -63,6 +63,7 @@ export const Panel = ({ active }: PanelProps) => { return ( ; export const SelectProjectsQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"SelectProjectsQuery"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"viewer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"accounts"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"avatarUrl"}},{"kind":"Field","name":{"kind":"Name","value":"projects"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"webUrl"}},{"kind":"Field","name":{"kind":"Name","value":"projectToken"}},{"kind":"Field","name":{"kind":"Name","value":"lastBuild"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"branch"}},{"kind":"Field","name":{"kind":"Name","value":"number"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const ProjectQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ProjectQuery"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"project"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"webUrl"}},{"kind":"Field","name":{"kind":"Name","value":"lastBuild"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"branch"}},{"kind":"Field","name":{"kind":"Name","value":"number"}}]}}]}}]}}]} as unknown as DocumentNode; -export const BuildDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Build"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"hasBuildId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"buildId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"branch"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"build"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"buildId"}}}],"directives":[{"kind":"Directive","name":{"kind":"Name","value":"include"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"if"},"value":{"kind":"Variable","name":{"kind":"Name","value":"hasBuildId"}}}]}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BuildFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"project"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"directives":[{"kind":"Directive","name":{"kind":"Name","value":"skip"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"if"},"value":{"kind":"Variable","name":{"kind":"Name","value":"hasBuildId"}}}]}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lastBuild"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"branches"},"value":{"kind":"ListValue","values":[{"kind":"Variable","name":{"kind":"Name","value":"branch"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BuildFields"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Test"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"result"}},{"kind":"Field","name":{"kind":"Name","value":"webUrl"}},{"kind":"Field","name":{"kind":"Name","value":"comparisons"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"result"}},{"kind":"Field","name":{"kind":"Name","value":"browser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"version"}}]}},{"kind":"Field","name":{"kind":"Name","value":"captureDiff"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"diffImage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"imageUrl"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"headCapture"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"captureImage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"imageUrl"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"viewport"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"isDefault"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"parameters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"viewport"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"isDefault"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"story"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"storyId"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BuildFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Build"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"number"}},{"kind":"Field","name":{"kind":"Name","value":"branch"}},{"kind":"Field","name":{"kind":"Name","value":"commit"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"browsers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StartedBuild"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"changeCount"},"name":{"kind":"Name","value":"testCount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"results"},"value":{"kind":"ListValue","values":[{"kind":"EnumValue","value":"ADDED"},{"kind":"EnumValue","value":"CHANGED"},{"kind":"EnumValue","value":"FIXED"}]}}]},{"kind":"Field","name":{"kind":"Name","value":"startedAt"}},{"kind":"Field","name":{"kind":"Name","value":"tests"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TestFields"}}]}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CompletedBuild"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"result"}},{"kind":"Field","alias":{"kind":"Name","value":"changeCount"},"name":{"kind":"Name","value":"testCount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"results"},"value":{"kind":"ListValue","values":[{"kind":"EnumValue","value":"ADDED"},{"kind":"EnumValue","value":"CHANGED"},{"kind":"EnumValue","value":"FIXED"}]}}]},{"kind":"Field","name":{"kind":"Name","value":"startedAt"}},{"kind":"Field","name":{"kind":"Name","value":"tests"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TestFields"}}]}}]}}]}}]}}]} as unknown as DocumentNode; \ No newline at end of file +export const BuildDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Build"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"hasBuildId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"buildId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"branch"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"build"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"buildId"}}}],"directives":[{"kind":"Directive","name":{"kind":"Name","value":"include"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"if"},"value":{"kind":"Variable","name":{"kind":"Name","value":"hasBuildId"}}}]}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BuildFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"project"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"directives":[{"kind":"Directive","name":{"kind":"Name","value":"skip"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"if"},"value":{"kind":"Variable","name":{"kind":"Name","value":"hasBuildId"}}}]}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"lastBuild"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"branches"},"value":{"kind":"ListValue","values":[{"kind":"Variable","name":{"kind":"Name","value":"branch"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BuildFields"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Test"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"result"}},{"kind":"Field","name":{"kind":"Name","value":"webUrl"}},{"kind":"Field","name":{"kind":"Name","value":"comparisons"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"result"}},{"kind":"Field","name":{"kind":"Name","value":"browser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"version"}}]}},{"kind":"Field","name":{"kind":"Name","value":"captureDiff"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"diffImage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"imageUrl"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"headCapture"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"captureImage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"imageUrl"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"viewport"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"isDefault"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"parameters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"viewport"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"isDefault"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"story"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"storyId"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BuildFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Build"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"number"}},{"kind":"Field","name":{"kind":"Name","value":"branch"}},{"kind":"Field","name":{"kind":"Name","value":"commit"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"browsers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StartedBuild"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"changeCount"},"name":{"kind":"Name","value":"testCount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"results"},"value":{"kind":"ListValue","values":[{"kind":"EnumValue","value":"ADDED"},{"kind":"EnumValue","value":"CHANGED"},{"kind":"EnumValue","value":"FIXED"}]}}]},{"kind":"Field","name":{"kind":"Name","value":"startedAt"}},{"kind":"Field","name":{"kind":"Name","value":"tests"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TestFields"}}]}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CompletedBuild"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"result"}},{"kind":"Field","alias":{"kind":"Name","value":"changeCount"},"name":{"kind":"Name","value":"testCount"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"results"},"value":{"kind":"ListValue","values":[{"kind":"EnumValue","value":"ADDED"},{"kind":"EnumValue","value":"CHANGED"},{"kind":"EnumValue","value":"FIXED"}]}}]},{"kind":"Field","name":{"kind":"Name","value":"startedAt"}},{"kind":"Field","name":{"kind":"Name","value":"tests"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TestFields"}}]}}]}}]}}]}}]} as unknown as DocumentNode; \ No newline at end of file diff --git a/src/screens/VisualTests/VisualTests.stories.tsx b/src/screens/VisualTests/VisualTests.stories.tsx index 6ad51449..fc7ea32e 100644 --- a/src/screens/VisualTests/VisualTests.stories.tsx +++ b/src/screens/VisualTests/VisualTests.stories.tsx @@ -251,6 +251,7 @@ const meta = { parameters: withBuild(passedBuild), args: { storyId: "button--primary", + projectId: "Project:id123", }, } satisfies Meta; @@ -277,6 +278,14 @@ export const NoChanges: Story = { }, }; +export const NoBuild: Story = { + parameters: { + ...withGraphQLQuery("Build", (req, res, ctx) => res(ctx.data({ build: null } as BuildQuery))), + // No design for this state + // ...withFigmaDesign(""), + }, +}; + export const Outdated: Story = { args: { isOutdated: true, diff --git a/src/screens/VisualTests/VisualTests.tsx b/src/screens/VisualTests/VisualTests.tsx index b4bdf228..e4238810 100644 --- a/src/screens/VisualTests/VisualTests.tsx +++ b/src/screens/VisualTests/VisualTests.tsx @@ -30,6 +30,7 @@ const QueryBuild = graphql(/* GraphQL */ ` ...BuildFields } project(id: $projectId) @skip(if: $hasBuildId) { + name lastBuild(branches: [$branch]) { ...BuildFields } @@ -113,6 +114,7 @@ const QueryBuild = graphql(/* GraphQL */ ` `); interface VisualTestsProps { + projectId: string; isOutdated?: boolean; isRunning?: boolean; lastDevBuildId?: string; @@ -131,16 +133,16 @@ export const VisualTests = ({ setAccessToken, setIsOutdated, setIsRunning, + projectId, storyId, }: VisualTestsProps) => { // TODO: Replace hardcoded projectId with useProjectId hook and parameters - const [projectId, setProjectId, projectIdChanged] = useProjectId(); const [{ data, fetching, error }, rerun] = useQuery({ query: QueryBuild, variables: { hasBuildId: !!lastDevBuildId, buildId: lastDevBuildId || "", - projectId: "643d59b4f51c48601c1df552", + projectId, branch: "test", }, }); @@ -152,7 +154,7 @@ export const VisualTests = ({ } }, [isRunning, setIsOutdated, setIsRunning, data]); - const build = (data?.build || data?.project.lastBuild) as BuildFieldsFragment; + const build = (data?.build || data?.project?.lastBuild) as BuildFieldsFragment; useEffect(() => { let interval: any; @@ -176,6 +178,18 @@ export const VisualTests = ({ )} {fetching && } + {!build && !fetching && !error && ( +
+ + + + Your project {data.project?.name} does not have any builds yet. Run a build a to + begin. + + + +
+ )}
@@ -209,23 +223,6 @@ export const VisualTests = ({ ); } - // If there is no lastBuild, show a prompt to run a build. This is a placeholder for now. - if (!data.project.lastBuild) { - return ( - -
- - - - Your project {data.project.name} does not have any builds yet. Run a build a to - begin. - - - -
-
- ); - } const allTests = ("tests" in build ? build.tests.nodes : []) as TestFieldsFragment[]; const tests = allTests.filter((test) => test.story?.storyId === storyId); From b39b0049d9989ff38a5934b8226e7f4810533c62 Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Thu, 13 Jul 2023 16:01:12 +1000 Subject: [PATCH 16/28] Use `main.js` and env for projectId + git info --- .storybook/main.ts | 8 --- package.json | 2 +- src/constants.ts | 9 ++-- src/index.ts | 71 ++++++++++++++++--------- src/screens/LinkProject/LinkProject.tsx | 3 +- src/utils/useProjectId.ts | 46 ++++------------ 6 files changed, 65 insertions(+), 74 deletions(-) diff --git a/.storybook/main.ts b/.storybook/main.ts index 9045d12b..5698df3c 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -1,7 +1,4 @@ import type { StorybookConfig } from "@storybook/react-vite"; - -const CHROMATIC_BASE_URL = process.env.CHROMATIC_BASE_URL || "https://www.chromatic.com"; - const config: StorybookConfig = { addons: [ { @@ -24,10 +21,6 @@ const config: StorybookConfig = { docs: { autodocs: "tag", }, - env: (config) => ({ - ...config, - CHROMATIC_BASE_URL, - }), framework: { name: "@storybook/react-vite", options: {}, @@ -36,5 +29,4 @@ const config: StorybookConfig = { stories: ["../src/**/*.stories.@(js|jsx|ts|tsx)"], logLevel: "debug", }; - export default config; diff --git a/package.json b/package.json index 278adf0f..218e6d16 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "release": "pnpm run build && auto shipit", "start": "run-p build:watch 'storybook --quiet'", "prerelease": "zx scripts/prepublish-checks.mjs", - "storybook": "storybook dev -p 6006" + "storybook": "CHROMATIC_ADDON_NAME='../dist/index.js' storybook dev -p 6006" }, "dependencies": { "@storybook/csf-tools": "^7.1.0-alpha.34", diff --git a/src/constants.ts b/src/constants.ts index cbc628eb..a6e7ced1 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -6,10 +6,13 @@ export const PANEL_ID = `${ADDON_ID}/panel`; export const ACCESS_TOKEN_KEY = `${ADDON_ID}/access-token/${CHROMATIC_BASE_URL}`; export const DEV_BUILD_ID_KEY = `${ADDON_ID}/dev-build-id`; -export const PROJECT_PARAM_KEY = "projectId"; - export const START_BUILD = `${ADDON_ID}/startBuild`; export const BUILD_STARTED = `${ADDON_ID}/buildStarted`; export const UPDATE_PROJECT = `${ADDON_ID}/updateProject`; -export const PROJECT_UPDATED = `${ADDON_ID}/projectUpdated`; \ No newline at end of file +export type UpdateProjectPayload = { + projectId: string; + projectToken: string; +}; + +export const PROJECT_UPDATED = `${ADDON_ID}/projectUpdated`; diff --git a/src/index.ts b/src/index.ts index f06631a5..90c0480a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,11 +1,18 @@ import type { Channel } from "@storybook/channels"; import { readConfig, writeConfig } from "@storybook/csf-tools"; +import { exec } from "child_process"; // eslint-disable-next-line import/no-unresolved import { run } from "chromatic/node"; +import { promisify } from "util"; -import { BUILD_STARTED, START_BUILD, UPDATE_PROJECT } from "./constants"; +import { BUILD_STARTED, START_BUILD, UPDATE_PROJECT, UpdateProjectPayload } from "./constants"; import { findConfig } from "./utils/storybook.config.utils"; +const { + CHROMATIC_BASE_URL = "https://www.chromatic.com", + CHROMATIC_ADDON_NAME = "@chromaui/addon-visual-tests", +} = process.env; + /** * to load the built addon in this test Storybook */ @@ -35,39 +42,53 @@ async function serverChannel(channel: Channel, { projectToken }: { projectToken: }); }); - channel.on(UPDATE_PROJECT, async (id, token) => { - - // update project id - // find config file path - const previewPath = await findConfig("preview"); - // Find config file - const PreviewConfig = await readConfig(previewPath); - - // Add parameters to config file - const previousProjectId = PreviewConfig.getFieldNode(["parameters", "projectId"]); - console.log("updating project id", previousProjectId, id); - PreviewConfig.setFieldValue(["parameters", "projectId"], id); - - // Write to config file - await writeConfig(PreviewConfig); - - - - // update project token in main.ts - this is not currently working, does not select the main.ts section correctly + channel.on(UPDATE_PROJECT, async ({ projectId, projectToken }: UpdateProjectPayload) => { const mainPath = await findConfig("main"); const MainConfig = await readConfig(mainPath); - // TODO: Get the correct field node - const addonsConfig = MainConfig.getFieldNode(["addons"]); - console.log(addonsConfig); + const addonsConfig = MainConfig.getFieldValue(["addons"]); + const updatedAddonsConfig = addonsConfig.map( + (addonConfig: string | { name: string; options?: Record }) => { + const fullConfig = typeof addonConfig === "string" ? { name: addonConfig } : addonConfig; + if (fullConfig.name === CHROMATIC_ADDON_NAME) { + return { + ...fullConfig, + options: { projectId, projectToken, ...fullConfig.options }, + }; + } + return addonConfig; + } + ); - MainConfig.setFieldValue(["addons", "projectToken"], token); + MainConfig.setFieldValue(["addons"], updatedAddonsConfig); + await writeConfig(MainConfig); }); return channel; } -module.exports = { +// TODO: use the chromatic CLI to get this info? Or should we make it core to SB? +const execPromise = promisify(exec); +async function getGitInfo() { + const branch = (await execPromise("git rev-parse --abbrev-ref HEAD")).stdout.trim(); + const commit = (await execPromise("git log -n 1 HEAD --format='%H'")).stdout.trim(); + return { branch, commit }; +} + +const config = { managerEntries, experimental_serverChannel: serverChannel, + // FIXME: we should only do this in dev + env: async (env: Record, { projectId }: { projectId: string }) => { + const { branch, commit } = await getGitInfo(); + return { + ...env, + CHROMATIC_BASE_URL, + CHROMATIC_PROJECT_ID: projectId || "", + GIT_BRANCH: branch, + GIT_COMMIT: commit, + }; + }, }; + +module.exports = config; diff --git a/src/screens/LinkProject/LinkProject.tsx b/src/screens/LinkProject/LinkProject.tsx index 63600e78..df85af38 100644 --- a/src/screens/LinkProject/LinkProject.tsx +++ b/src/screens/LinkProject/LinkProject.tsx @@ -115,10 +115,9 @@ function SelectProject({ const handleSelectProject = React.useCallback( (project: SelectProjectsQueryQuery["viewer"]["accounts"][number]["projects"][number]) => { setSelectingProject(true); - const { id: projectId, code: projectToken } = project; + const { id: projectId, projectToken } = project; onSelectProjectId(projectId, projectToken); const timer = setTimeout(() => { - console.log("resetting timer"); setSelectingProject(false); }, 1000); return () => clearTimeout(timer); diff --git a/src/utils/useProjectId.ts b/src/utils/useProjectId.ts index 4fabe241..894915bd 100644 --- a/src/utils/useProjectId.ts +++ b/src/utils/useProjectId.ts @@ -1,9 +1,9 @@ -import { useChannel, useParameter } from "@storybook/manager-api"; +import { useChannel } from "@storybook/manager-api"; import React from "react"; -import { PROJECT_PARAM_KEY, PROJECT_UPDATED, UPDATE_PROJECT } from "../constants"; +import { UPDATE_PROJECT, UpdateProjectPayload } from "../constants"; -let inMemoryProjectId: string | null = null; +const { CHROMATIC_PROJECT_ID } = process.env; export const useProjectId = (): [ projectId: string, @@ -11,39 +11,15 @@ export const useProjectId = (): [ projectIdChanged: boolean, clearProjectIdChanged: () => void ] => { - // - // Ideally this should only be configured once in main.ts. We'll need to figure out how to do that. This should at least read from that config for now. - const configuredProjectId = useParameter(PROJECT_PARAM_KEY, null); + const [projectId, setProjectId] = React.useState(CHROMATIC_PROJECT_ID); + const [projectIdChanged, setProjectIdChanged] = React.useState(false); - const [projectId, setProjectId] = React.useState(null); + const emit = useChannel({}); - const actualProjectId = React.useMemo( - () => projectId || inMemoryProjectId || configuredProjectId, - [projectId, configuredProjectId] - ); - - // TODO: This is where we need to update the main.ts config with the projectId - const emit = useChannel({ - [PROJECT_UPDATED]: (selectedProjectId: string, projectToken) => { - console.log("projectId selected from emit", selectedProjectId); - setProjectId(selectedProjectId); - }, - }); - - const updateProject = (id: string, projectToken: string) => { - emit(UPDATE_PROJECT, id, projectToken); - inMemoryProjectId = id; - setProjectId(inMemoryProjectId); + const updateProject = (newProjectId: string, projectToken: string) => { + emit(UPDATE_PROJECT, { projectId: newProjectId, projectToken } as UpdateProjectPayload); + setProjectId(newProjectId); + setProjectIdChanged(true); }; - - // Used for now to prompt user to update project id in main.ts manually - const [clearedProjectIdChanged, setClearedProjectIdChanged] = React.useState(false); - const projectIdChanged = configuredProjectId !== actualProjectId && !!clearedProjectIdChanged; - - console.log({ - configuredProjectId, - projectId, - projectIdChanged, - }); - return [actualProjectId, updateProject, projectIdChanged, () => setClearedProjectIdChanged(true)]; + return [projectId, updateProject, projectIdChanged, () => setProjectIdChanged(false)]; }; From b61c87d97f3715a3b3c1542f43cecb9d614b0bd0 Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Thu, 13 Jul 2023 16:06:19 +1000 Subject: [PATCH 17/28] Use update project token for builds --- src/index.ts | 47 ++++++++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/src/index.ts b/src/index.ts index 90c0480a..08042c0a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -20,7 +20,11 @@ function managerEntries(entry: string[] = []) { return [...entry, require.resolve("./manager.mjs")]; } -async function serverChannel(channel: Channel, { projectToken }: { projectToken: string }) { +async function serverChannel( + channel: Channel, + { projectToken: initialProjectToken }: { projectToken: string } +) { + let projectToken = initialProjectToken; channel.on(START_BUILD, async () => { let sent = false; await run({ @@ -42,27 +46,32 @@ async function serverChannel(channel: Channel, { projectToken }: { projectToken: }); }); - channel.on(UPDATE_PROJECT, async ({ projectId, projectToken }: UpdateProjectPayload) => { - const mainPath = await findConfig("main"); - const MainConfig = await readConfig(mainPath); + channel.on( + UPDATE_PROJECT, + async ({ projectId, projectToken: updatedProjectToken }: UpdateProjectPayload) => { + projectToken = updatedProjectToken; - const addonsConfig = MainConfig.getFieldValue(["addons"]); - const updatedAddonsConfig = addonsConfig.map( - (addonConfig: string | { name: string; options?: Record }) => { - const fullConfig = typeof addonConfig === "string" ? { name: addonConfig } : addonConfig; - if (fullConfig.name === CHROMATIC_ADDON_NAME) { - return { - ...fullConfig, - options: { projectId, projectToken, ...fullConfig.options }, - }; + const mainPath = await findConfig("main"); + const MainConfig = await readConfig(mainPath); + + const addonsConfig = MainConfig.getFieldValue(["addons"]); + const updatedAddonsConfig = addonsConfig.map( + (addonConfig: string | { name: string; options?: Record }) => { + const fullConfig = typeof addonConfig === "string" ? { name: addonConfig } : addonConfig; + if (fullConfig.name === CHROMATIC_ADDON_NAME) { + return { + ...fullConfig, + options: { projectId, projectToken, ...fullConfig.options }, + }; + } + return addonConfig; } - return addonConfig; - } - ); + ); - MainConfig.setFieldValue(["addons"], updatedAddonsConfig); - await writeConfig(MainConfig); - }); + MainConfig.setFieldValue(["addons"], updatedAddonsConfig); + await writeConfig(MainConfig); + } + ); return channel; } From 77dd4812a46adad22a041ea6c0da8ed4205af8c5 Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Thu, 13 Jul 2023 16:13:43 +1000 Subject: [PATCH 18/28] Pass branch into `VisualTests` --- src/Panel.tsx | 3 +++ src/screens/VisualTests/VisualTests.tsx | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Panel.tsx b/src/Panel.tsx index 4524be9c..39d1484f 100644 --- a/src/Panel.tsx +++ b/src/Panel.tsx @@ -14,6 +14,8 @@ interface PanelProps { active: boolean; } +const { GIT_BRANCH } = process.env; + export const Panel = ({ active }: PanelProps) => { const [accessToken, setAccessToken] = useAccessToken(); @@ -64,6 +66,7 @@ export const Panel = ({ active }: PanelProps) => { { // TODO: Replace hardcoded projectId with useProjectId hook and parameters @@ -143,7 +145,7 @@ export const VisualTests = ({ hasBuildId: !!lastDevBuildId, buildId: lastDevBuildId || "", projectId, - branch: "test", + branch: branch || "", }, }); From 96a5b3c06b7010859780f1bdc24b71fc386723ff Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Thu, 13 Jul 2023 20:52:38 +1000 Subject: [PATCH 19/28] Switch to default export --- src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 08042c0a..650ca35d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -100,4 +100,4 @@ const config = { }, }; -module.exports = config; +export default config; From 5246be424fba251c9a04b5637713b67b34cc479b Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Thu, 13 Jul 2023 20:55:05 +1000 Subject: [PATCH 20/28] Don't set env in prod --- src/index.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/index.ts b/src/index.ts index 650ca35d..2fb4358b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -87,8 +87,12 @@ async function getGitInfo() { const config = { managerEntries, experimental_serverChannel: serverChannel, - // FIXME: we should only do this in dev - env: async (env: Record, { projectId }: { projectId: string }) => { + env: async ( + env: Record, + { projectId, configType }: { projectId: string; configType: "development" | "production" } + ) => { + if (configType === "production") return env; + const { branch, commit } = await getGitInfo(); return { ...env, From a4f2299b0977fdee0605e8f7aee4d9aafb2b7ce1 Mon Sep 17 00:00:00 2001 From: Gert Hengeveld Date: Fri, 14 Jul 2023 09:57:18 +0200 Subject: [PATCH 21/28] Fix formatting --- .storybook/preview.tsx | 186 +++++++++++++++++++--------------- scripts/prepublish-checks.mjs | 2 +- 2 files changed, 105 insertions(+), 83 deletions(-) diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index c046eb9e..897fdc72 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -1,4 +1,12 @@ -import { Global, ThemeProvider, themes, createReset, convert, styled, useTheme } from "@storybook/theming"; +import { + Global, + ThemeProvider, + themes, + createReset, + convert, + styled, + useTheme, +} from "@storybook/theming"; import type { Preview } from "@storybook/react"; import { initialize, mswDecorator } from "msw-storybook-addon"; import React from "react"; @@ -7,40 +15,45 @@ import React from "react"; initialize({ onUnhandledRequest(req) { if (req.url.origin !== document.location.origin) { - console.error(`[MSW] %s %s %s (UNHANDLED)`, new Date().toTimeString().slice(0, 8), req.method.toUpperCase(), req.url.href); + console.error( + `[MSW] %s %s %s (UNHANDLED)`, + new Date().toTimeString().slice(0, 8), + req.method.toUpperCase(), + req.url.href + ); } - } + }, }); const Panels = styled.div<{ orientation: "right" | "bottom"; -}>(({ - orientation -}) => ({ - flexDirection: orientation === "right" ? "row" : "column" -}), { - display: "flex", - justifyContent: "center", - alignItems: "center", - gap: 40, - margin: 40 -}); +}>( + ({ orientation }) => ({ + flexDirection: orientation === "right" ? "row" : "column", + }), + { + display: "flex", + justifyContent: "center", + alignItems: "center", + gap: 40, + margin: 40, + } +); const Panel = styled.div<{ orientation: "right" | "bottom"; -}>(({ - orientation -}) => ({ - width: orientation === "right" ? 420 : 880, - height: orientation === "right" ? 640 : 300, - overflow: "auto" -}), ({ - theme -}) => ({ - position: "relative", - outline: `1px solid ${theme.color.border}`, - background: theme.background.content, - color: theme.color.defaultText, - fontSize: theme.typography.size.s2 - 1 -})); +}>( + ({ orientation }) => ({ + width: orientation === "right" ? 420 : 880, + height: orientation === "right" ? 640 : 300, + overflow: "auto", + }), + ({ theme }) => ({ + position: "relative", + outline: `1px solid ${theme.color.border}`, + background: theme.background.content, + color: theme.color.defaultText, + fontSize: theme.typography.size.s2 - 1, + }) +); const ThemedSetRoot = () => { const theme = useTheme(); React.useEffect(() => { @@ -55,57 +68,61 @@ export const decorators = [ mswDecorator, // Render two panels side-by-side or stacked, depending on selected orientation // Note this assumes any play functions are equipped to deal with multiple canvases - (StoryFn, { - globals, - parameters - }) => { + (StoryFn, { globals, parameters }) => { const theme = globals.theme || parameters.theme || "right"; - return theme === "light" || theme === "dark" ? - - - - - : <> - + return theme === "light" || theme === "dark" ? ( + + + - + ) : ( + <> - - - - - - - - + + - - ; - }]; + + + + + + + + + + + + + + ); + }, +]; export const parameters: Preview["parameters"] = { actions: { - argTypesRegex: "^on[A-Z].*" + argTypesRegex: "^on[A-Z].*", }, backgrounds: { - disable: true + disable: true, }, chromatic: { - viewports: [960] + viewports: [960], }, controls: { matchers: { color: /(background|color)$/i, - date: /Date$/ - } + date: /Date$/, + }, }, - layout: "fullscreen" + layout: "fullscreen", }; export const globalTypes = { theme: { @@ -114,23 +131,28 @@ export const globalTypes = { toolbar: { icon: "sidebaralt", title: "Theme", - items: [{ - value: "light", - icon: "circlehollow", - title: "Light" - }, { - value: "dark", - icon: "circle", - title: "Dark" - }, { - value: "right", - icon: "sidebaralt", - title: "Right 2-up" - }, { - value: "bottom", - icon: "bottombar", - title: "Bottom 2-up" - }] - } - } + items: [ + { + value: "light", + icon: "circlehollow", + title: "Light", + }, + { + value: "dark", + icon: "circle", + title: "Dark", + }, + { + value: "right", + icon: "sidebaralt", + title: "Right 2-up", + }, + { + value: "bottom", + icon: "bottombar", + title: "Bottom 2-up", + }, + ], + }, + }, }; diff --git a/scripts/prepublish-checks.mjs b/scripts/prepublish-checks.mjs index 5860052a..9546d906 100644 --- a/scripts/prepublish-checks.mjs +++ b/scripts/prepublish-checks.mjs @@ -55,4 +55,4 @@ if ((await $`cat README.md | grep -E ${readmeTestStrings}`.exitCode) == 0) { exitCode = 1; } -process.exit(exitCode); \ No newline at end of file +process.exit(exitCode); From 10f2b5e89251b02b5ee4caba80a422325ec605da Mon Sep 17 00:00:00 2001 From: Gert Hengeveld Date: Fri, 14 Jul 2023 10:05:46 +0200 Subject: [PATCH 22/28] Fix formatting --- .storybook/preview.tsx | 41 +++++++---------------------------------- 1 file changed, 7 insertions(+), 34 deletions(-) diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index 897fdc72..8596c85c 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -24,9 +24,7 @@ initialize({ } }, }); -const Panels = styled.div<{ - orientation: "right" | "bottom"; -}>( +const Panels = styled.div<{ orientation: "right" | "bottom" }>( ({ orientation }) => ({ flexDirection: orientation === "right" ? "row" : "column", }), @@ -38,9 +36,7 @@ const Panels = styled.div<{ margin: 40, } ); -const Panel = styled.div<{ - orientation: "right" | "bottom"; -}>( +const Panel = styled.div<{ orientation: "right" | "bottom" }>( ({ orientation }) => ({ width: orientation === "right" ? 420 : 880, height: orientation === "right" ? 640 : 300, @@ -73,14 +69,7 @@ export const decorators = [ return theme === "light" || theme === "dark" ? ( - + @@ -132,26 +121,10 @@ export const globalTypes = { icon: "sidebaralt", title: "Theme", items: [ - { - value: "light", - icon: "circlehollow", - title: "Light", - }, - { - value: "dark", - icon: "circle", - title: "Dark", - }, - { - value: "right", - icon: "sidebaralt", - title: "Right 2-up", - }, - { - value: "bottom", - icon: "bottombar", - title: "Bottom 2-up", - }, + { value: "light", icon: "circlehollow", title: "Light" }, + { value: "dark", icon: "circle", title: "Dark" }, + { value: "right", icon: "sidebaralt", title: "Right 2-up" }, + { value: "bottom", icon: "bottombar", title: "Bottom 2-up" }, ], }, }, From 4d2869b851e8495f2850964810181ba08be9261e Mon Sep 17 00:00:00 2001 From: Gert Hengeveld Date: Fri, 14 Jul 2023 10:08:41 +0200 Subject: [PATCH 23/28] Fix formatting --- .storybook/preview.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index 8596c85c..8351c41e 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -24,6 +24,7 @@ initialize({ } }, }); + const Panels = styled.div<{ orientation: "right" | "bottom" }>( ({ orientation }) => ({ flexDirection: orientation === "right" ? "row" : "column", @@ -36,6 +37,7 @@ const Panels = styled.div<{ orientation: "right" | "bottom" }>( margin: 40, } ); + const Panel = styled.div<{ orientation: "right" | "bottom" }>( ({ orientation }) => ({ width: orientation === "right" ? 420 : 880, @@ -50,6 +52,7 @@ const Panel = styled.div<{ orientation: "right" | "bottom" }>( fontSize: theme.typography.size.s2 - 1, }) ); + const ThemedSetRoot = () => { const theme = useTheme(); React.useEffect(() => { @@ -59,9 +62,11 @@ const ThemedSetRoot = () => { }); return null; }; + export const decorators = [ // Provide the MSW addon decorator globally mswDecorator, + // Render two panels side-by-side or stacked, depending on selected orientation // Note this assumes any play functions are equipped to deal with multiple canvases (StoryFn, { globals, parameters }) => { @@ -95,6 +100,7 @@ export const decorators = [ ); }, ]; + export const parameters: Preview["parameters"] = { actions: { argTypesRegex: "^on[A-Z].*", @@ -113,6 +119,7 @@ export const parameters: Preview["parameters"] = { }, layout: "fullscreen", }; + export const globalTypes = { theme: { name: "Theme", From 870c0a808db33ef052f55b14afb86a10eac9d188 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Fri, 14 Jul 2023 10:13:28 +0200 Subject: [PATCH 24/28] cleanup --- src/utils/storybook.config.utils.ts | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/utils/storybook.config.utils.ts b/src/utils/storybook.config.utils.ts index fe1214a0..8cd6917f 100644 --- a/src/utils/storybook.config.utils.ts +++ b/src/utils/storybook.config.utils.ts @@ -2,18 +2,15 @@ import fs from "node:fs/promises"; const SUPPORTED_EXTENSIONS = ["js", "ts", "tsx", "jsx"] as const; +const pathExists = (f: string) => + fs.stat(f).then( + () => true, + () => false + ); + export const findConfig = async (prefix: string) => { const filenames = SUPPORTED_EXTENSIONS.map((ext) => `.storybook/${prefix}.${ext}`); - const exists = await Promise.all( - filenames.map(async (f) => { - try { - const stats = await fs.stat(f); - return true; - } catch (e) { - return false; - } - }) - ); + const exists = await Promise.all(filenames.map(pathExists)); const idx = exists.findIndex((e) => e); if (idx === -1) return filenames[0]; // create a JS file From 7a86c94b850aaf5520ce5917574a612c72cca643 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Fri, 14 Jul 2023 10:28:14 +0200 Subject: [PATCH 25/28] add slug partially implemented Co-authored-by: Gert Hengeveld --- src/Panel.tsx | 3 ++- src/index.ts | 10 +++++++--- src/screens/VisualTests/VisualTests.tsx | 3 +++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/Panel.tsx b/src/Panel.tsx index 39d1484f..53cd9538 100644 --- a/src/Panel.tsx +++ b/src/Panel.tsx @@ -14,7 +14,7 @@ interface PanelProps { active: boolean; } -const { GIT_BRANCH } = process.env; +const { GIT_BRANCH, GIT_SLUG } = process.env; export const Panel = ({ active }: PanelProps) => { const [accessToken, setAccessToken] = useAccessToken(); @@ -67,6 +67,7 @@ export const Panel = ({ active }: PanelProps) => { { if (configType === "production") return env; - const { branch, commit } = await getGitInfo(); + const { branch, commit, slug } = await getGitInfo(); return { ...env, CHROMATIC_BASE_URL, CHROMATIC_PROJECT_ID: projectId || "", GIT_BRANCH: branch, GIT_COMMIT: commit, + GIT_SLUG: slug, }; }, }; diff --git a/src/screens/VisualTests/VisualTests.tsx b/src/screens/VisualTests/VisualTests.tsx index 6825ce11..11519c90 100644 --- a/src/screens/VisualTests/VisualTests.tsx +++ b/src/screens/VisualTests/VisualTests.tsx @@ -116,6 +116,7 @@ const QueryBuild = graphql(/* GraphQL */ ` interface VisualTestsProps { projectId: string; branch?: string; + slug?: string; isOutdated?: boolean; isRunning?: boolean; lastDevBuildId?: string; @@ -136,6 +137,7 @@ export const VisualTests = ({ setIsRunning, projectId, branch, + slug, storyId, }: VisualTestsProps) => { // TODO: Replace hardcoded projectId with useProjectId hook and parameters @@ -146,6 +148,7 @@ export const VisualTests = ({ buildId: lastDevBuildId || "", projectId, branch: branch || "", + // slug: slug || "", }, }); From f60be53fe371608b8c6ce0d1ef37746b2f20db30 Mon Sep 17 00:00:00 2001 From: Gert Hengeveld Date: Fri, 14 Jul 2023 10:28:22 +0200 Subject: [PATCH 26/28] Update src/Panel.tsx --- src/Panel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Panel.tsx b/src/Panel.tsx index 53cd9538..df5e5de9 100644 --- a/src/Panel.tsx +++ b/src/Panel.tsx @@ -55,7 +55,7 @@ export const Panel = ({ active }: PanelProps) => { ); - if (projectId && projectIdChanged) { + if (projectIdChanged) { return ( From 6df7fd8288330bf526cd7f04cc14bc71dbe672ad Mon Sep 17 00:00:00 2001 From: Gert Hengeveld Date: Fri, 14 Jul 2023 16:47:09 +0200 Subject: [PATCH 27/28] Validate slug and pass it to the project.lastBuild field --- src/index.ts | 11 +++++++---- src/screens/VisualTests/VisualTests.tsx | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/index.ts b/src/index.ts index 60e7066a..93f60338 100644 --- a/src/index.ts +++ b/src/index.ts @@ -81,10 +81,13 @@ const execPromise = promisify(exec); async function getGitInfo() { const branch = (await execPromise("git rev-parse --abbrev-ref HEAD")).stdout.trim(); const commit = (await execPromise("git log -n 1 HEAD --format='%H'")).stdout.trim(); - const result = (await execPromise("git config --get remote.origin.url")).stdout.trim(); - const downcasedResult = result.toLowerCase(); - const [, slug] = downcasedResult.match(/([^/:]+\/[^/]+?)(\.git)?$/) || []; - return { branch, commit, slug }; + const origin = (await execPromise("git config --get remote.origin.url")).stdout.trim(); + + const [, slug] = origin.toLowerCase().match(/([^/:]+\/[^/]+?)(\.git)?$/) || []; + const [ownerName, repoName, ...rest] = slug ? slug.split("/") : []; + const isValidSlug = !!ownerName && !!repoName && !rest.length; + + return { branch, commit, slug: isValidSlug ? slug : "" }; } const config = { diff --git a/src/screens/VisualTests/VisualTests.tsx b/src/screens/VisualTests/VisualTests.tsx index 11519c90..c8550aeb 100644 --- a/src/screens/VisualTests/VisualTests.tsx +++ b/src/screens/VisualTests/VisualTests.tsx @@ -148,7 +148,7 @@ export const VisualTests = ({ buildId: lastDevBuildId || "", projectId, branch: branch || "", - // slug: slug || "", + ...(slug ? { slug } : {}), }, }); From f8c401c3e0669b1dfa23117433c7eb4f435dee17 Mon Sep 17 00:00:00 2001 From: Gert Hengeveld Date: Sat, 15 Jul 2023 09:55:09 +0200 Subject: [PATCH 28/28] Fix missing dependency and bad import --- package.json | 1 + pnpm-lock.yaml | 63 ++++++++++--------- .../VisualTests/RenderSettings.stories.tsx | 2 +- 3 files changed, 35 insertions(+), 31 deletions(-) diff --git a/package.json b/package.json index 218e6d16..d5db8597 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "devDependencies": { "@graphql-codegen/cli": "^4.0.1", "@graphql-codegen/client-preset": "^4.0.1", + "@graphql-typed-document-node/core": "^3.2.0", "@storybook/addon-actions": "^7.1.0-alpha.34", "@storybook/addon-essentials": "^7.1.0-alpha.34", "@storybook/addon-interactions": "^7.1.0-alpha.34", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1ba4b041..d42afe19 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -55,6 +55,9 @@ devDependencies: '@graphql-codegen/client-preset': specifier: ^4.0.1 version: 4.0.1(graphql@16.6.0) + '@graphql-typed-document-node/core': + specifier: ^3.2.0 + version: 3.2.0(graphql@16.6.0) '@storybook/addon-actions': specifier: ^7.1.0-alpha.34 version: 7.1.0-alpha.34(react-dom@18.0.0)(react@18.0.0) @@ -4122,11 +4125,11 @@ packages: '@storybook/channels': 7.1.0-alpha.34 '@storybook/client-logger': 7.1.0-alpha.34 - /@storybook/channel-postmessage@7.1.0-rc.1: - resolution: {integrity: sha512-bX3j4EQIftkDMeXzL9JCAKSBR6FxAC6YfjavGBdhBIrOAdAqgP8opX65PJHpmN7j93hhaPqkMxsrJrtvqfTFXQ==} + /@storybook/channel-postmessage@7.1.0-rc.2: + resolution: {integrity: sha512-XQQFN9xYdUwAdWcQopBaNPkj65bSwyBsGEDKFQd1zvwvEv4QKiMWqQVnx84yXfEUu9b4jVolyyB5tQGLS4/6Fw==} dependencies: - '@storybook/channels': 7.1.0-rc.1 - '@storybook/client-logger': 7.1.0-rc.1 + '@storybook/channels': 7.1.0-rc.2 + '@storybook/client-logger': 7.1.0-rc.2 dev: true /@storybook/channels@7.0.21: @@ -4143,11 +4146,11 @@ packages: telejson: 7.1.0 tiny-invariant: 1.3.1 - /@storybook/channels@7.1.0-rc.1: - resolution: {integrity: sha512-I7qwZdtTr+0q4/6x0UhqNy3LuvMZzHW9+cJrJwlpxfXDv9KHXQc4t1FJrWgeUymTreckLUWkwuO0DrRa8s594g==} + /@storybook/channels@7.1.0-rc.2: + resolution: {integrity: sha512-P9u0l5T0N2zMo387f0+eRBZ29Jg2+mfalBcsvTs2TYR9Kb/plQlEXhvIRxJm3W/OeEvS2vxixN2YOFeZGWlUYg==} dependencies: - '@storybook/client-logger': 7.1.0-rc.1 - '@storybook/core-events': 7.1.0-rc.1 + '@storybook/client-logger': 7.1.0-rc.2 + '@storybook/core-events': 7.1.0-rc.2 '@storybook/global': 5.0.0 qs: 6.11.2 telejson: 7.1.0 @@ -4213,8 +4216,8 @@ packages: dependencies: '@storybook/global': 5.0.0 - /@storybook/client-logger@7.1.0-rc.1: - resolution: {integrity: sha512-9gIDb6K7cotqZie+BbhCFg0Lk2yMHkJEnC0/0cPXO7FqRhx75kHrGIsdrZShylZUjLoLQZQoV5z2mr085NO4Eg==} + /@storybook/client-logger@7.1.0-rc.2: + resolution: {integrity: sha512-O0JLRCFrL7YLMd8sBHdLCqbJBLxW8nCrj7cOwkxt1HY73vy4u/VRujmVZbElaG5QJIoZNUZERyL+oSChthKWlg==} dependencies: '@storybook/global': 5.0.0 dev: true @@ -4323,8 +4326,8 @@ packages: /@storybook/core-events@7.1.0-alpha.34: resolution: {integrity: sha512-b9+2dm8kz7d1DzyhCjhgWAwbOSvKakI/Jcnp/ml4CEZhOTx77fHgfRxEPwxMc1g+xHM8YZGSLgAb857ShJwfEw==} - /@storybook/core-events@7.1.0-rc.1: - resolution: {integrity: sha512-cuWpVQxJLLHZ8dxO17yySQdaj20VFYZC/Cm4guKv+/RH0aI+kALkoe5zJ6JnUSjwPujrZI6c/9bHVsjqi3czzQ==} + /@storybook/core-events@7.1.0-rc.2: + resolution: {integrity: sha512-qoYLet2G2vNxpVKPTqLovGNoNSNjID7V2vk9hdUis51osOiVHlgJwXxkz33fUOI18y1JbYoVRM8LZaFaMHGF2g==} dev: true /@storybook/core-server@7.1.0-alpha.34: @@ -4517,14 +4520,14 @@ packages: '@storybook/preview-api': 7.1.0-alpha.34 dev: true - /@storybook/instrumenter@7.1.0-rc.1: - resolution: {integrity: sha512-o/WTL1loHHd6ynay/BCowjC5EXz5ce4GfjeZkEkAMiSAdskPV2jCnxYaz6Sq9+wMwZkEEmt36pgvQQjqi3Od1g==} + /@storybook/instrumenter@7.1.0-rc.2: + resolution: {integrity: sha512-xmtSNDfSWRkaMIcAlm3ga6ZjHoZgs0xBR3wEgDBtcygaWewvTtuBK4MxgT5NpJjPxGpjVakzjxGoITLQgrk0Kw==} dependencies: - '@storybook/channels': 7.1.0-rc.1 - '@storybook/client-logger': 7.1.0-rc.1 - '@storybook/core-events': 7.1.0-rc.1 + '@storybook/channels': 7.1.0-rc.2 + '@storybook/client-logger': 7.1.0-rc.2 + '@storybook/core-events': 7.1.0-rc.2 '@storybook/global': 5.0.0 - '@storybook/preview-api': 7.1.0-rc.1 + '@storybook/preview-api': 7.1.0-rc.2 dev: true /@storybook/linter-config@3.1.2(@typescript-eslint/parser@5.59.11)(eslint@8.42.0)(prettier@2.8.8)(remark@13.0.0)(typescript@4.9.5): @@ -4681,16 +4684,16 @@ packages: ts-dedent: 2.2.0 util-deprecate: 1.0.2 - /@storybook/preview-api@7.1.0-rc.1: - resolution: {integrity: sha512-Wme9IZvMolGtyAbbx6Z31+AHxP5EltizYiSmBwxZrX5uxMSvNMS8MbB0A85mrnw9I0U0MORSqDeclUQ8S/CL5g==} + /@storybook/preview-api@7.1.0-rc.2: + resolution: {integrity: sha512-E+X5I1BzFF5aTmclkmoVBYQB+JMx1SUCdkzl+GGX9Wxh5JmyTQDI90ynv0k1aJNvPmC5mS2EMmeqcYoz+AuYZA==} dependencies: - '@storybook/channel-postmessage': 7.1.0-rc.1 - '@storybook/channels': 7.1.0-rc.1 - '@storybook/client-logger': 7.1.0-rc.1 - '@storybook/core-events': 7.1.0-rc.1 + '@storybook/channel-postmessage': 7.1.0-rc.2 + '@storybook/channels': 7.1.0-rc.2 + '@storybook/client-logger': 7.1.0-rc.2 + '@storybook/core-events': 7.1.0-rc.2 '@storybook/csf': 0.1.1 '@storybook/global': 5.0.0 - '@storybook/types': 7.1.0-rc.1 + '@storybook/types': 7.1.0-rc.2 '@types/qs': 6.9.7 dequal: 2.0.3 lodash: 4.17.21 @@ -4833,8 +4836,8 @@ packages: /@storybook/testing-library@0.0.14-next.1: resolution: {integrity: sha512-1CAl40IKIhcPaCC4pYCG0b9IiYNymktfV/jTrX7ctquRY3akaN7f4A1SippVHosksft0M+rQTFE0ccfWW581fw==} dependencies: - '@storybook/client-logger': 7.1.0-rc.1 - '@storybook/instrumenter': 7.1.0-rc.1 + '@storybook/client-logger': 7.1.0-rc.2 + '@storybook/instrumenter': 7.1.0-rc.2 '@testing-library/dom': 8.20.0 '@testing-library/user-event': 13.5.0(@testing-library/dom@8.20.0) ts-dedent: 2.2.0 @@ -4870,10 +4873,10 @@ packages: '@types/express': 4.17.17 file-system-cache: 2.3.0 - /@storybook/types@7.1.0-rc.1: - resolution: {integrity: sha512-pm5xlgY37nPRm+SZJrSHJAk5oGgVA1ft/5T4zaw/C4Hkh2PtzlvP16CG1kdVug1AHRKWUSKNJYELdzYsCbvUhg==} + /@storybook/types@7.1.0-rc.2: + resolution: {integrity: sha512-pd19JdNEE+yuqBNlvk61423ZWQHrbsVafEmiJLD0wL4oWBZts7dijZPYiyrw+Fhw/EuXWsLg0edizBk2Id+JmA==} dependencies: - '@storybook/channels': 7.1.0-rc.1 + '@storybook/channels': 7.1.0-rc.2 '@types/babel__core': 7.20.1 '@types/express': 4.17.17 file-system-cache: 2.3.0 diff --git a/src/screens/VisualTests/RenderSettings.stories.tsx b/src/screens/VisualTests/RenderSettings.stories.tsx index 75c91d85..b397847d 100644 --- a/src/screens/VisualTests/RenderSettings.stories.tsx +++ b/src/screens/VisualTests/RenderSettings.stories.tsx @@ -1,7 +1,7 @@ import type { Meta, StoryObj } from "@storybook/react"; import { graphql } from "msw"; -import { Project, ProjectQueryQuery } from "src/gql/graphql"; +import { ProjectQueryQuery } from "../../gql/graphql"; import { storyWrapper } from "../../utils/graphQLClient"; import { withFigmaDesign } from "../../utils/withFigmaDesign"; import { RenderSettings } from "./RenderSettings";