Skip to content

Commit 23a8cf6

Browse files
authored
Show connected client version in the devtools (#1373)
1 parent 71ff31b commit 23a8cf6

28 files changed

+3425
-1758
lines changed

.changeset/mighty-masks-chew.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"apollo-client-devtools": minor
3+
---
4+
5+
Show connected Apollo Client version in the devtools. Hovering over the version gives you the release notes for that version, or the pull request information if its a snapshot release.

jest.config.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,9 @@ export default {
1515
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$":
1616
"<rootDir>/src/__mocks__/fileMock.js",
1717
"\\.(css|less)$": "<rootDir>/src/__mocks__/styleMock.js",
18+
"react-markdown": "<rootDir>/src/__mocks__/react-markdown.js",
19+
"rehype-raw": "<rootDir>/src/__mocks__/noop.js",
20+
"remark-gfm": "<rootDir>/src/__mocks__/noop.js",
21+
"remark-github": "<rootDir>/src/__mocks__/noop.js",
1822
},
1923
};

package-lock.json

Lines changed: 2552 additions & 1721 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,9 @@
4242
"@apollo/tailwind-preset": "^0.1.13",
4343
"@headlessui/react": "^1.7.17",
4444
"@headlessui/tailwindcss": "^0.2.0",
45+
"@radix-ui/react-hover-card": "^1.0.7",
4546
"@radix-ui/react-slot": "^1.0.2",
46-
"@radix-ui/react-tabs": "^1.0.2",
47+
"@radix-ui/react-tabs": "^1.0.4",
4748
"@radix-ui/react-tooltip": "^1.0.7",
4849
"@xstate/fsm": "^2.1.0",
4950
"class-variance-authority": "^0.7.0",
@@ -57,8 +58,12 @@
5758
"react-copy-to-clipboard": "^5.1.0",
5859
"react-dom": "^18.2.0",
5960
"react-json-tree": "^0.18.0",
61+
"react-markdown": "^9.0.1",
6062
"react-resizable-panels": "^1.0.0",
6163
"react-syntax-highlighter": "^15.5.0",
64+
"rehype-raw": "^7.0.0",
65+
"remark-gfm": "^4.0.0",
66+
"remark-github": "^12.0.0",
6267
"tailwind-merge": "^2.3.0",
6368
"zen-observable": "^0.10.0"
6469
},

src/__mocks__/noop.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = () => {};

src/__mocks__/react-markdown.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = function ReactMarkdown({ children }) {
2+
return children;
3+
};

src/application/App.tsx

Lines changed: 50 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ import {
3030
SECTIONS,
3131
} from "./components/GitHubIssueLink";
3232
import { Tooltip } from "./components/Tooltip";
33+
import { Badge } from "./components/Badge";
34+
import { GitHubReleaseHoverCard } from "./components/GitHubReleaseHoverCard";
35+
import { isSnapshotRelease, parseSnapshotRelease } from "./utilities/github";
3336

3437
const panelWindow = getPanelActor(window);
3538

@@ -80,6 +83,7 @@ const GET_OPERATION_COUNTS: TypedDocumentNode<
8083
GetOperationCountsVariables
8184
> = gql`
8285
query GetOperationCounts {
86+
clientVersion @client
8387
watchedQueries @client {
8488
count
8589
}
@@ -109,6 +113,8 @@ export const App = () => {
109113
const [embeddedExplorerIFrame, setEmbeddedExplorerIFrame] =
110114
useState<HTMLIFrameElement | null>(null);
111115

116+
const clientVersion = data?.clientVersion;
117+
112118
useEffect(() => {
113119
let timeout: NodeJS.Timeout;
114120
// Don't show connected message on the first render if we are already
@@ -173,29 +179,50 @@ export const App = () => {
173179
<Tabs.Trigger value={Screens.Cache}>Cache</Tabs.Trigger>
174180
<Tabs.Trigger value={Screens.Explorer}>Explorer</Tabs.Trigger>
175181

176-
<ButtonGroup className="ml-auto flex-1 justify-end">
177-
<Tooltip content="Report an issue">
178-
<Button
179-
aria-label="Report an issue"
180-
variant="hidden"
181-
size="sm"
182-
icon={<IconGitHubSolid />}
183-
asChild
184-
>
185-
<GitHubIssueLink labels={[LABELS.bug]} body={ISSUE_BODY} />
186-
</Button>
187-
</Tooltip>
188-
189-
<Tooltip content="Settings">
190-
<Button
191-
aria-label="Settings"
192-
size="sm"
193-
variant="hidden"
194-
onClick={() => setSettingsOpen(true)}
195-
icon={<IconSettings />}
196-
/>
197-
</Tooltip>
198-
</ButtonGroup>
182+
<div className="ml-auto flex-1 justify-end flex items-center gap-2">
183+
{clientVersion && (
184+
<GitHubReleaseHoverCard version={clientVersion}>
185+
<a
186+
className="no-underline"
187+
href={
188+
isSnapshotRelease(clientVersion)
189+
? `https://github.com/apollographql/apollo-client/pull/${parseSnapshotRelease(clientVersion).prNumber}`
190+
: `https://github.com/apollographql/apollo-client/releases/tag/v${clientVersion}`
191+
}
192+
target="_blank"
193+
rel="noreferrer"
194+
>
195+
<Badge variant="info" className="cursor-pointer">
196+
Apollo Client <span className="lowercase">v</span>
197+
{clientVersion}
198+
</Badge>
199+
</a>
200+
</GitHubReleaseHoverCard>
201+
)}
202+
<ButtonGroup>
203+
<Tooltip content="Report an issue">
204+
<Button
205+
aria-label="Report an issue"
206+
variant="hidden"
207+
size="sm"
208+
icon={<IconGitHubSolid />}
209+
asChild
210+
>
211+
<GitHubIssueLink labels={[LABELS.bug]} body={ISSUE_BODY} />
212+
</Button>
213+
</Tooltip>
214+
215+
<Tooltip content="Settings">
216+
<Button
217+
aria-label="Settings"
218+
size="sm"
219+
variant="hidden"
220+
onClick={() => setSettingsOpen(true)}
221+
icon={<IconSettings />}
222+
/>
223+
</Tooltip>
224+
</ButtonGroup>
225+
</div>
199226
<SettingsModal open={settingsOpen} onOpen={setSettingsOpen} />
200227
</Tabs.List>
201228
{/**

src/application/components/Badge.tsx

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { forwardRef, type ReactNode } from "react";
2+
import type { VariantProps } from "class-variance-authority";
3+
import { cva } from "class-variance-authority";
4+
import { twMerge } from "tailwind-merge";
5+
import type { OmitNull } from "../types/utils";
6+
7+
interface BadgeProps extends Variants {
8+
className?: string;
9+
children: ReactNode;
10+
}
11+
12+
type Variants = OmitNull<Required<VariantProps<typeof badge>>>;
13+
14+
const badge = cva(
15+
["rounded-sm", "py-0.5", "px-1", "uppercase", "font-bold", "text-xs"],
16+
{
17+
variants: {
18+
variant: {
19+
info: [
20+
"bg-info",
21+
"text-info",
22+
"dark:bg-info-dark",
23+
"dark:text-info-dark",
24+
],
25+
success: [
26+
"bg-success",
27+
"text-success",
28+
"dark:bg-success-dark",
29+
"dark:text-success-dark",
30+
],
31+
error: [
32+
"bg-error",
33+
"text-error",
34+
"dark:bg-error-dark",
35+
"dark:text-error-dark",
36+
],
37+
beta: [
38+
"bg-beta",
39+
"text-beta",
40+
"dark:bg-beta-dark",
41+
"dark:text-beta-dark",
42+
],
43+
warning: [
44+
"bg-warning",
45+
"text-warning",
46+
"dark:bg-warning-dark",
47+
"dark:text-warning-dark",
48+
],
49+
neutral: [
50+
"bg-neutral",
51+
"text-neutral",
52+
"dark:bg-neutral-dark",
53+
"dark:text-neutral-dark",
54+
],
55+
primary: [
56+
"bg-primary",
57+
"text-primary",
58+
"dark:bg-primary-dark",
59+
"dark:text-primary-dark",
60+
],
61+
brand: [
62+
"bg-primary",
63+
"text-primary",
64+
"dark:bg-primary-dark",
65+
"dark:text-primary-dark",
66+
"border",
67+
"border-brand-horizon",
68+
],
69+
},
70+
},
71+
}
72+
);
73+
74+
export const Badge = forwardRef<HTMLSpanElement, BadgeProps>(
75+
({ className, children, variant, ...props }, ref) => {
76+
return (
77+
<span
78+
{...props}
79+
className={twMerge(badge({ variant }), className)}
80+
ref={ref}
81+
>
82+
{children}
83+
</span>
84+
);
85+
}
86+
);

src/application/components/Cache/__tests__/Cache.test.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ describe("Cache component tests", () => {
7676
client.resetStore();
7777

7878
writeData({
79+
clientVersion: "3.10.0",
7980
queries: [],
8081
mutations: [],
8182
cache: CACHE_DATA,
@@ -124,6 +125,7 @@ describe("Cache component tests", () => {
124125
describe("Search", () => {
125126
beforeEach(() => {
126127
writeData({
128+
clientVersion: "3.10.0",
127129
queries: [],
128130
mutations: [],
129131
cache: CACHE_DATA,

src/application/components/CodeBlock.tsx

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ interface SyntaxHighlighterProps {
1313
className?: string;
1414
language: Language;
1515
code: string;
16+
copyable?: boolean;
1617
}
1718

1819
const { code } = colors.tokens;
@@ -133,6 +134,7 @@ const getTheme = (mode: ColorTheme): PrismTheme => {
133134
export const CodeBlock = ({
134135
code,
135136
className,
137+
copyable = true,
136138
language,
137139
}: SyntaxHighlighterProps) => {
138140
const theme = useReactiveVar(colorTheme);
@@ -169,17 +171,14 @@ export const CodeBlock = ({
169171
);
170172
}}
171173
</Highlight>
172-
<CopyToClipboard text={code}>
173-
<Button
174-
size="sm"
175-
variant="hidden"
176-
data-testid="copy-query-strin"
177-
className="sticky top-0"
178-
>
179-
<IconCopy className="w-4" />
180-
Copy
181-
</Button>
182-
</CopyToClipboard>
174+
{copyable && (
175+
<CopyToClipboard text={code}>
176+
<Button size="sm" variant="hidden" className="sticky top-0">
177+
<IconCopy className="w-4" />
178+
Copy
179+
</Button>
180+
</CopyToClipboard>
181+
)}
183182
</div>
184183
);
185184
};

0 commit comments

Comments
 (0)