Skip to content

Commit

Permalink
Updated API + improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
ghengeveld committed Jun 21, 2023
1 parent d586b4c commit f160bc7
Show file tree
Hide file tree
Showing 18 changed files with 402 additions and 221 deletions.
11 changes: 6 additions & 5 deletions src/Panel.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React from "react";

import { PANEL_ID } from "./constants";
import { Authentication } from "./screens/Authentication/Authentication";
import { LinkProject } from "./screens/LinkProject/LinkProject";
import { VisualTests } from "./screens/VisualTests/VisualTests";
import { client, Provider, useAccessToken } from "./utils/graphQLClient";

interface PanelProps {
Expand All @@ -13,14 +14,14 @@ export const Panel = ({ active }: PanelProps) => {

// 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 <div hidden />;
if (!active) return <div hidden key={PANEL_ID} />;

// Render the Authentication flow if the user is not signed in.
if (!accessToken) return <Authentication setAccessToken={setAccessToken} />;
if (!accessToken) return <Authentication key={PANEL_ID} setAccessToken={setAccessToken} />;

return (
<Provider value={client}>
<LinkProject />
<Provider key={PANEL_ID} value={client}>
<VisualTests setAccessToken={setAccessToken} />
</Provider>
);
};
2 changes: 1 addition & 1 deletion src/components/Accordions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export const CloseIcon = styled(Icons)({
marginLeft: "auto",
});

export const CloseButton = styled(IconButton)({
export const CloseButton: typeof IconButton = styled(IconButton)({
margin: -5,
marginLeft: "auto",
});
2 changes: 1 addition & 1 deletion src/components/Badge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Badge as BaseBadge } from "@storybook/components";
import { styled } from "@storybook/theming";
import { ReactNode } from "react";

export const Badge = styled(BaseBadge)<{ children?: ReactNode }>({
export const Badge: typeof BaseBadge = styled(BaseBadge)<{ children?: ReactNode }>({
padding: "4px 8px",
margin: "0 6px",
});
49 changes: 24 additions & 25 deletions src/components/BrowserSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from "react";

import { aggregate } from "../constants";
import { ComparisonResult } from "../gql/graphql";
import { Browser, BrowserInfo, ComparisonResult } from "../gql/graphql";
import { aggregateResult } from "../utils/aggregateResult";
import { ArrowIcon } from "./icons/ArrowIcon";
import { ChromeIcon } from "./icons/ChromeIcon";
import { EdgeIcon } from "./icons/EdgeIcon";
Expand All @@ -10,52 +10,51 @@ import { SafariIcon } from "./icons/SafariIcon";
import { StatusDot, StatusDotWrapper } from "./StatusDot";
import { TooltipMenu } from "./TooltipMenu";

const supportedBrowsers = {
chrome: { title: "Chrome", icon: <ChromeIcon alt="Chrome" /> },
firefox: { title: "Firefox", icon: <FirefoxIcon alt="Firefox" /> },
safari: { title: "Safari", icon: <SafariIcon alt="Safari" /> },
edge: { title: "Edge", icon: <EdgeIcon alt="Edge" /> },
const browserIcons = {
[Browser.Chrome]: <ChromeIcon alt="Chrome" />,
[Browser.Firefox]: <FirefoxIcon alt="Firefox" />,
[Browser.Safari]: <SafariIcon alt="Safari" />,
[Browser.Edge]: <EdgeIcon alt="Edge" />,
} as const;

type Browser = keyof typeof supportedBrowsers;
type BrowserData = Pick<BrowserInfo, "id" | "key" | "name">;

interface BrowserSelectorProps {
browserResults: Record<string, ComparisonResult>;
onSelectBrowser: (browser: string) => void;
browserResults: { browser: BrowserData; result: ComparisonResult }[];
onSelectBrowser: (browser: BrowserData) => void;
}

export const BrowserSelector = ({ browserResults, onSelectBrowser }: BrowserSelectorProps) => {
const [selected, setSelected] = React.useState(Object.keys(browserResults)[0]);
const [selected, setSelected] = React.useState(browserResults[0].browser);

const handleSelect = React.useCallback(
(browser: string) => {
(browser: BrowserData) => {
setSelected(browser);
onSelectBrowser(browser);
},
[onSelectBrowser]
);

const links = Object.entries(browserResults)
.filter(([browser]) => browser in supportedBrowsers)
.map(([browser, status]) => ({
const links = browserResults
.filter(({ browser }) => browser.key in browserIcons)
.map(({ browser, result }) => ({
active: selected === browser,
id: browser,
id: browser.id,
onClick: () => handleSelect(browser),
right: status !== ComparisonResult.Equal && <StatusDot status={status} />,
title: supportedBrowsers[browser as Browser].title,
right: result !== ComparisonResult.Equal && <StatusDot status={result} />,
title: browser.name,
}));

const status = aggregate(Object.values(browserResults));
if (!status) return null;
const aggregate = aggregateResult(browserResults.map(({ result }) => result));
if (!aggregate) return null;

const icon = browserIcons[selected.key];
return (
<TooltipMenu placement="bottom" links={links}>
{status === ComparisonResult.Equal ? (
supportedBrowsers[selected as Browser].icon
{aggregate === ComparisonResult.Equal ? (
icon
) : (
<StatusDotWrapper status={status}>
{supportedBrowsers[selected as Browser].icon}
</StatusDotWrapper>
<StatusDotWrapper status={aggregate}>{icon}</StatusDotWrapper>
)}
<ArrowIcon icon="arrowdown" />
</TooltipMenu>
Expand Down
7 changes: 6 additions & 1 deletion src/components/Button.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Button as BaseButton } from "@storybook/components";
import { css, styled } from "@storybook/theming";

export const Button = styled(BaseButton)<{ link?: boolean }>(
export const Button = styled(BaseButton)<{ link?: boolean; tertiary?: boolean }>(
{
"&&": {
borderRadius: 4,
Expand All @@ -25,5 +25,10 @@ export const Button = styled(BaseButton)<{ link?: boolean }>(
outline: `1px solid ${theme.color.secondary}`,
},
},
}),
({ tertiary }) =>
tertiary &&
css({
"&&:hover": { boxShadow: "none" },
})
);
7 changes: 5 additions & 2 deletions src/components/IconButton.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { IconButton as BaseIconButton } from "@storybook/components";
import { styled } from "@storybook/theming";
import { ComponentProps } from "react";

export const IconButton = styled(BaseIconButton)<{
interface IconButtonProps extends ComponentProps<typeof BaseIconButton> {
active?: boolean;
as?: string;
secondary?: boolean;
status?: "warning";
}>(
}

export const IconButton: React.FC<IconButtonProps> = styled(BaseIconButton)<IconButtonProps>(
({ active, secondary, theme }) => ({
display: "inline-flex",
alignItems: "center",
Expand Down
2 changes: 1 addition & 1 deletion src/components/SnapshotImage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const SnapshotImage = styled.div({
alignItems: "center",
background: "#fff",
minHeight: 100,
padding: 2,
margin: 2,

img: {
maxWidth: "100%",
Expand Down
30 changes: 15 additions & 15 deletions src/components/StatusDot.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { css, styled } from "@storybook/theming";
import React from "react";

import { ComparisonResult, TestStatus } from "../constants";
import { ComparisonResult, TestStatus } from "../gql/graphql";

interface StatusDotProps {
status?: TestStatus | ComparisonResult;
Expand All @@ -14,20 +14,20 @@ const Dot = styled.div<StatusDotProps & { overlay?: boolean }>(
height: 6,
borderRadius: "50%",
background: {
[TestStatus.IN_PROGRESS]: "transparent",
[TestStatus.PASSED]: theme.color.positive,
[TestStatus.PENDING]: theme.color.gold,
[TestStatus.ACCEPTED]: theme.color.positive,
[TestStatus.DENIED]: theme.color.positive,
[TestStatus.BROKEN]: theme.color.negative,
[TestStatus.FAILED]: theme.color.negative,
[ComparisonResult.EQUAL]: theme.color.positive,
[ComparisonResult.FIXED]: theme.color.positive,
[ComparisonResult.ADDED]: theme.color.gold,
[ComparisonResult.CHANGED]: theme.color.gold,
[ComparisonResult.REMOVED]: theme.color.gold,
[ComparisonResult.CAPTURE_ERROR]: theme.color.negative,
[ComparisonResult.SYSTEM_ERROR]: theme.color.negative,
[TestStatus.InProgress]: "transparent",
[TestStatus.Passed]: theme.color.positive,
[TestStatus.Pending]: theme.color.gold,
[TestStatus.Accepted]: theme.color.positive,
[TestStatus.Denied]: theme.color.positive,
[TestStatus.Broken]: theme.color.negative,
[TestStatus.Failed]: theme.color.negative,
[ComparisonResult.Equal]: theme.color.positive,
[ComparisonResult.Fixed]: theme.color.positive,
[ComparisonResult.Added]: theme.color.gold,
[ComparisonResult.Changed]: theme.color.gold,
[ComparisonResult.Removed]: theme.color.gold,
[ComparisonResult.CaptureError]: theme.color.negative,
[ComparisonResult.SystemError]: theme.color.negative,
}[status],
}),
({ overlay, theme }) =>
Expand Down
2 changes: 1 addition & 1 deletion src/components/SuffixInput.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Input } from "@storybook/design-system";
import { color, styled } from "@storybook/theming";
import { styled } from "@storybook/theming";
import React, { ComponentProps, ReactNode } from "react";

const InputWrapper = styled.div(({ theme }) => ({
Expand Down
30 changes: 16 additions & 14 deletions src/components/ViewportSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,50 +1,52 @@
import { Icon } from "@storybook/design-system";
import React from "react";

import { aggregate } from "../constants";
import { ComparisonResult } from "../gql/graphql";
import { ComparisonResult, ViewportInfo } from "../gql/graphql";
import { aggregateResult } from "../utils/aggregateResult";
import { ArrowIcon } from "./icons/ArrowIcon";
import { StatusDot, StatusDotWrapper } from "./StatusDot";
import { TooltipMenu } from "./TooltipMenu";

type ViewportData = Pick<ViewportInfo, "id" | "name">;

interface ViewportSelectorProps {
viewportResults: Record<string, ComparisonResult>;
onSelectViewport: (viewport: string) => void;
viewportResults: { viewport: ViewportData; result: ComparisonResult }[];
onSelectViewport: (viewport: ViewportData) => void;
}

export const ViewportSelector = ({ viewportResults, onSelectViewport }: ViewportSelectorProps) => {
const [selected, setSelected] = React.useState(Object.keys(viewportResults)[0]);
const [selected, setSelected] = React.useState(viewportResults[0].viewport);

const handleSelect = React.useCallback(
(viewport: string) => {
(viewport: ViewportData) => {
setSelected(viewport);
onSelectViewport(viewport);
},
[onSelectViewport]
);

const aggregateResult = aggregate(Object.values(viewportResults));
if (!aggregateResult) return null;
const aggregate = aggregateResult(viewportResults.map(({ result }) => result));
if (!aggregate) return null;

return (
<TooltipMenu
placement="bottom"
links={Object.entries(viewportResults).map(([viewport, result]) => ({
id: `viewport-${viewport}`,
title: viewport,
links={viewportResults.map(({ viewport, result }) => ({
id: viewport.id,
title: viewport.name,
right: result !== ComparisonResult.Equal && <StatusDot status={result} />,
onClick: () => handleSelect(viewport),
active: selected === viewport,
}))}
>
{aggregateResult === ComparisonResult.Equal ? (
{aggregate === ComparisonResult.Equal ? (
<Icon icon="grow" />
) : (
<StatusDotWrapper status={aggregateResult}>
<StatusDotWrapper status={aggregate}>
<Icon icon="grow" />
</StatusDotWrapper>
)}
{selected}
{selected.name}
<ArrowIcon icon="arrowdown" />
</TooltipMenu>
);
Expand Down
53 changes: 29 additions & 24 deletions src/components/icons/StatusIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,35 @@
import { Icon } from "@storybook/design-system";
import { styled } from "@storybook/theming";
import React from "react";
// import React from "react";

import { BuildStatus } from "../../constants";
// import { BuildStatus } from "../../gql/graphql";

const StyledIcon = styled(Icon)<{ icon: "passed" | "changed" | "failed" }>(({ icon, theme }) => ({
width: 12,
height: 12,
margin: "3px 6px",
verticalAlign: "top",
export const StatusIcon = styled(Icon)<{ icon: "passed" | "changed" | "failed" }>(
({ icon, theme }) => ({
width: 12,
height: 12,
margin: "3px 6px",
verticalAlign: "top",

color: {
passed: theme.color.positive,
changed: theme.color.warning,
failed: theme.color.negative,
}[icon],
}));
color: {
passed: theme.color.positive,
changed: theme.color.warning,
failed: theme.color.negative,
}[icon],
})
);

export const StatusIcon = ({ status }: { status: BuildStatus }) =>
({
[BuildStatus.IN_PROGRESS]: null,
[BuildStatus.PASSED]: <StyledIcon icon="passed" />,
[BuildStatus.PENDING]: <StyledIcon icon="changed" />,
[BuildStatus.ACCEPTED]: <StyledIcon icon="passed" />,
[BuildStatus.DENIED]: <StyledIcon icon="failed" />,
[BuildStatus.BROKEN]: <StyledIcon icon="failed" />,
[BuildStatus.FAILED]: <StyledIcon icon="failed" />,
[BuildStatus.CANCELLED]: <StyledIcon icon="failed" />,
}[status]);
// export const StatusIcon = ({ status }: { status: BuildStatus }) =>
// ({
// [BuildStatus.Announced]: null,
// [BuildStatus.Published]: null,
// [BuildStatus.Prepared]: null,
// [BuildStatus.InProgress]: null,
// [BuildStatus.Passed]: <StyledIcon icon="passed" />,
// [BuildStatus.Pending]: <StyledIcon icon="changed" />,
// [BuildStatus.Accepted]: <StyledIcon icon="passed" />,
// [BuildStatus.Denied]: <StyledIcon icon="failed" />,
// [BuildStatus.Broken]: <StyledIcon icon="failed" />,
// [BuildStatus.Failed]: <StyledIcon icon="failed" />,
// [BuildStatus.Cancelled]: <StyledIcon icon="failed" />,
// }[status]);
Loading

0 comments on commit f160bc7

Please sign in to comment.