Skip to content

Commit

Permalink
Add loading indicator for each tab and add error screens for errors i…
Browse files Browse the repository at this point in the history
…n each tab (#1459)
  • Loading branch information
jerelmiller authored Aug 20, 2024
1 parent 756cf06 commit b4a600b
Show file tree
Hide file tree
Showing 18 changed files with 511 additions and 243 deletions.
5 changes: 5 additions & 0 deletions .changeset/giant-toys-tan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"apollo-client-devtools": patch
---

Show a loading indicator when the extension is loading client data.
5 changes: 5 additions & 0 deletions .changeset/moody-pots-pull.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"apollo-client-devtools": patch
---

Show an error screen if an error occurs while getting client data. This will provide a helpful suggestion with a link to open a prefilled GitHub issue with the error that occurred.
12 changes: 12 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"react": "^18.2.0",
"react-copy-to-clipboard": "^5.1.0",
"react-dom": "^18.2.0",
"react-error-boundary": "^4.0.13",
"react-json-tree": "^0.19.0",
"react-markdown": "^9.0.1",
"react-resizable-panels": "^1.0.0",
Expand Down
66 changes: 57 additions & 9 deletions src/application/App.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { useEffect, useState } from "react";
import type { ReactNode } from "react";
import type { TypedDocumentNode } from "@apollo/client";
import { useReactiveVar, gql, useQuery } from "@apollo/client";
import { useMachine } from "@xstate/react";
import { ErrorBoundary } from "react-error-boundary";

import { currentScreen, Screens } from "./components/Layouts/Navigation";
import { Queries } from "./components/Queries/Queries";
Expand Down Expand Up @@ -37,6 +39,8 @@ import { Select } from "./components/Select";
import { Divider } from "./components/Divider";
import { useActorEvent } from "./hooks/useActorEvent";
import { removeClient } from ".";
import { PageError } from "./components/PageError";
import { SidebarLayout } from "./components/Layouts/SidebarLayout";

const APP_QUERY: TypedDocumentNode<AppQuery, AppQueryVariables> = gql`
query AppQuery {
Expand Down Expand Up @@ -273,24 +277,68 @@ export const App = () => {
className="flex-1 overflow-hidden"
value={Screens.Queries}
>
<Queries
clientId={selectedClientId}
explorerIFrame={embeddedExplorerIFrame}
/>
<TabErrorBoundary remarks="Error on Queries tab:">
<Queries
clientId={selectedClientId}
explorerIFrame={embeddedExplorerIFrame}
/>
</TabErrorBoundary>
</Tabs.Content>
<Tabs.Content
className="flex-1 overflow-hidden"
value={Screens.Mutations}
>
<Mutations
clientId={selectedClientId}
explorerIFrame={embeddedExplorerIFrame}
/>
<TabErrorBoundary remarks="Error on Mutations tab:">
<Mutations
clientId={selectedClientId}
explorerIFrame={embeddedExplorerIFrame}
/>
</TabErrorBoundary>
</Tabs.Content>
<Tabs.Content className="flex-1 overflow-hidden" value={Screens.Cache}>
<Cache clientId={selectedClientId} />
<TabErrorBoundary remarks="Error on Cache tab:">
<Cache clientId={selectedClientId} />
</TabErrorBoundary>
</Tabs.Content>
</Tabs>
</>
);
};

interface TabErrorBoundaryProps {
children?: ReactNode;
remarks?: string;
}

function TabErrorBoundary({ children, remarks }: TabErrorBoundaryProps) {
return (
<ErrorBoundary
fallbackRender={({ error }) => {
return (
<SidebarLayout>
<SidebarLayout.Main className="!overflow-y-auto">
<PageError>
<PageError.Content>
<PageError.Title>
We&apos;ve run into an unexpected error
</PageError.Title>
<PageError.Body>
Please try closing and reopening the browser devtools and
refreshing the page. If the issue persists, please{" "}
<PageError.GitHubLink error={error} remarks={remarks}>
open an issue
</PageError.GitHubLink>{" "}
to help us diagnose the error.
</PageError.Body>
</PageError.Content>
<PageError.Details error={error} />
</PageError>
</SidebarLayout.Main>
</SidebarLayout>
);
}}
>
{children}
</ErrorBoundary>
);
}
30 changes: 30 additions & 0 deletions src/application/assets/error-planet.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 29 additions & 4 deletions src/application/components/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ import type { AsChildProps } from "../types/props";
import { cva, type VariantProps } from "class-variance-authority";
import { twMerge } from "tailwind-merge";
import type { OmitNull } from "../types/utils";
import { Spinner } from "./Spinner";

type NativeButtonProps = ComponentPropsWithoutRef<"button">;

type ButtonProps = AsChildProps<NativeButtonProps> &
Variants & {
className?: string;
icon?: ReactElement<{ "aria-hidden": boolean; className?: string }>;
loading?: boolean;
loadingText?: string;
};

type Variants = OmitNull<Required<VariantProps<typeof button>>>;
Expand Down Expand Up @@ -112,23 +115,45 @@ const iconSize = cva([], {

export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
function Button(
{ asChild, className, children, variant, size, icon, ...props },
{
asChild,
className,
children,
variant,
size,
icon,
loading,
loadingText,
...props
},
ref
) {
const Component = asChild ? Slot : "button";
const content = loading ? loadingText ?? children : children;

return (
<Component
{...props}
ref={ref}
className={twMerge(button({ variant, size }), className)}
disabled={loading || (props as NativeButtonProps).disabled}
>
{isValidElement(icon) &&
{loading ? (
<Spinner size="xs" />
) : (
isValidElement(icon) &&
cloneElement(icon, {
"aria-hidden": true,
className: twMerge(iconSize({ size }), icon.props.className),
})}
<Slottable>{children}</Slottable>
})
)}
{asChild ? (
<Slottable>{content}</Slottable>
) : content && isValidElement(icon) ? (
<span>{content}</span>
) : (
content
)}
</Component>
);
}
Expand Down
32 changes: 19 additions & 13 deletions src/application/components/Cache/Cache.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import IconArrowRight from "@apollo/icons/small/IconArrowRight.svg";

import { SidebarLayout } from "../Layouts/SidebarLayout";
import { SearchField } from "../SearchField";
import { Loading } from "./common/Loading";
import type { GetCache, GetCacheVariables } from "../../types/gql";
import type { JSONObject } from "../../types/json";
import { JSONTreeViewer } from "../JSONTreeViewer";
Expand All @@ -24,6 +23,7 @@ import { ListItem } from "../ListItem";
import { getRootCacheIds } from "./common/utils";
import HighlightMatch from "../HighlightMatch";
import { useActorEvent } from "../../hooks/useActorEvent";
import { PageSpinner } from "../PageSpinner";

const { Sidebar, Main } = SidebarLayout;

Expand Down Expand Up @@ -63,12 +63,18 @@ export function Cache({ clientId }: CacheProps) {
const [searchTerm, setSearchTerm] = useState("");
const cacheId = useSyncExternalStore(history.listen, history.getCurrent);

const { loading, data, startPolling, stopPolling } = useQuery(GET_CACHE, {
variables: { id: clientId as string },
skip: clientId == null,
pollInterval: 500,
fetchPolicy: "cache-and-network",
});
const { loading, data, error, startPolling, stopPolling } = useQuery(
GET_CACHE,
{
variables: { id: clientId as string },
skip: clientId == null,
pollInterval: 500,
}
);

if (error) {
throw error;
}

useActorEvent("panelHidden", () => stopPolling());
useActorEvent("panelShown", () => startPolling(500));
Expand Down Expand Up @@ -140,7 +146,7 @@ export function Cache({ clientId }: CacheProps) {
</ButtonGroup>
<CopyButton
size="sm"
text={JSON.stringify(cache[cacheId])}
text={JSON.stringify(cacheItem)}
className={clsx({ invisible: !cacheItem })}
/>
</div>
Expand All @@ -154,12 +160,10 @@ export function Cache({ clientId }: CacheProps) {
</h1>
</div>
</>
) : (
<EmptyMessage className="m-auto mt-20" />
)}
) : null}

{loading ? (
<Loading />
<PageSpinner />
) : cacheItem ? (
<JSONTreeViewer
data={cacheItem}
Expand All @@ -186,7 +190,9 @@ export function Cache({ clientId }: CacheProps) {
This cache entry was either removed from the cache or does not
exist.
</Alert>
) : null}
) : (
<EmptyMessage className="m-auto mt-20" />
)}
</Main>
</SidebarLayout>
);
Expand Down
5 changes: 0 additions & 5 deletions src/application/components/Cache/common/Loading.tsx

This file was deleted.

2 changes: 1 addition & 1 deletion src/application/components/Layouts/SidebarLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ interface SidebarLayoutProps {

interface SidebarProps {
className?: string;
children: ReactNode;
children?: ReactNode;
}

const SidebarLayout = ({ children }: SidebarLayoutProps) => {
Expand Down
Loading

0 comments on commit b4a600b

Please sign in to comment.