Skip to content

Commit cffbc5d

Browse files
committed
Put the advanced connection settings behind a disclosure
1 parent a4090a8 commit cffbc5d

6 files changed

Lines changed: 165 additions & 64 deletions

File tree

‎docs/features/connections.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ For guides on connecting to specific databases, see [Connecting to databases](..
1717
- **AWS IAM Auth Enabled:** Check this box if connecting to Amazon Neptune using IAM Auth and SigV4 signed requests
1818
- **Service Type:** Choose the service type
1919
- **AWS Region:** Specify the AWS region where the Neptune cluster is hosted (e.g., us-east-1)
20+
21+
The next two settings are grouped under an **Advanced options** section that you expand to reach. It starts expanded when the connection you are editing already overrides one of them, so an existing override is never hidden from you.
22+
2023
- **Fetch Timeout:** Specify the timeout for the fetch request
2124
- **Neighbor Expansion Limit:** Specify the default limit for neighbor expansion. This will override the app setting for neighbor expansion.
2225

‎docs/guides/troubleshooting.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ There are multiple sources of timeouts.
117117

118118
Graph Explorer distinguishes two kinds of timeout and shows a different message for each.
119119

120-
**Fetch timeout exceeded** means the request didn't finish within the connection's own Fetch Timeout setting. This is a client-side limit you configure yourself in the connection's settings, under "Enable Fetch Timeout". Increase the Fetch Timeout value, or retry the request.
120+
**Fetch timeout exceeded** means the request didn't finish within the connection's own Fetch Timeout setting. This is a client-side limit you configure yourself in this connection's advanced options, under "Enable Fetch Timeout". Increase the Fetch Timeout value, or retry the request.
121121

122122
**Database query timed out** means the database itself stopped the query because it ran longer than the database's configured query timeout. For Neptune, this is controlled by the DB cluster parameter group. Increase the query timeout there, or retry the request.
123123

‎packages/graph-explorer/src/modules/CreateConnection/CreateConnection.test.tsx‎

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
11
// @vitest-environment happy-dom
2+
3+
import type { ConnectionConfig } from "@shared/types";
4+
25
import { render, screen, waitFor } from "@testing-library/react";
36
import userEvent from "@testing-library/user-event";
47
import { describe, expect, test, vi } from "vitest";
58

69
import { TooltipProvider } from "@/components";
7-
import { configurationAtom, getAppStore } from "@/core";
10+
import {
11+
type ConfigurationContextProps,
12+
configurationAtom,
13+
createNewConfigurationId,
14+
getAppStore,
15+
} from "@/core";
816
import { createQueryClient } from "@/core/queryClient";
917
import { mergeConfiguration } from "@/core/StateProvider/configuration";
1018
import { createRandomRawConfiguration, TestProvider } from "@/utils/testing";
@@ -26,6 +34,14 @@ function renderCreateConnection(ui: React.ReactElement) {
2634
return store;
2735
}
2836

37+
/** The advanced settings are behind a disclosure, so their content is unmounted until it opens. */
38+
async function openAdvancedOptions(user: ReturnType<typeof userEvent.setup>) {
39+
const trigger = screen.getByRole("button", { name: "Advanced options" });
40+
expect(trigger).toHaveAttribute("aria-expanded", "false");
41+
await user.click(trigger);
42+
expect(trigger).toHaveAttribute("aria-expanded", "true");
43+
}
44+
2945
describe("CreateConnection", () => {
3046
test("removes newlines and surrounding whitespace from URL fields", async () => {
3147
const user = userEvent.setup();
@@ -62,6 +78,7 @@ describe("CreateConnection", () => {
6278
test("labels the override field Neighbor Expansion Limit", async () => {
6379
const user = userEvent.setup();
6480
renderCreateConnection(<CreateConnection onClose={vi.fn()} />);
81+
await openAdvancedOptions(user);
6582

6683
await user.click(
6784
screen.getByRole("checkbox", {
@@ -74,6 +91,61 @@ describe("CreateConnection", () => {
7491
).toBeInTheDocument();
7592
});
7693

94+
test("keeps the advanced options collapsed until the user expands them", async () => {
95+
const user = userEvent.setup();
96+
renderCreateConnection(<CreateConnection onClose={vi.fn()} />);
97+
98+
expect(
99+
screen.queryByRole("checkbox", { name: /Enable Fetch Timeout/ }),
100+
).not.toBeInTheDocument();
101+
102+
await openAdvancedOptions(user);
103+
104+
expect(
105+
screen.getByRole("checkbox", { name: /Enable Fetch Timeout/ }),
106+
).toBeInTheDocument();
107+
expect(
108+
screen.getByRole("checkbox", {
109+
name: /Override Default Neighbor Expansion Limit/,
110+
}),
111+
).toBeInTheDocument();
112+
});
113+
114+
test("opens the advanced options when the connection already overrides one", () => {
115+
const configId = createNewConfigurationId();
116+
const store = getAppStore();
117+
const connection: ConnectionConfig = {
118+
url: "https://proxy.example.com",
119+
graphDbUrl: "",
120+
queryEngine: "gremlin",
121+
fetchTimeoutMs: 30000,
122+
};
123+
store.set(
124+
configurationAtom,
125+
new Map([[configId, { id: configId, connection }]]),
126+
);
127+
128+
render(
129+
<TestProvider client={createQueryClient()} store={store}>
130+
<TooltipProvider>
131+
<CreateConnection
132+
existingConfig={
133+
{ id: configId, connection } as ConfigurationContextProps
134+
}
135+
onClose={vi.fn()}
136+
/>
137+
</TooltipProvider>
138+
</TestProvider>,
139+
);
140+
141+
expect(
142+
screen.getByRole("button", { name: "Advanced options" }),
143+
).toHaveAttribute("aria-expanded", "true");
144+
expect(
145+
screen.getByRole("checkbox", { name: /Enable Fetch Timeout/ }),
146+
).toBeChecked();
147+
});
148+
77149
test("rejects a URL that is empty after normalization", async () => {
78150
const user = userEvent.setup();
79151
const store = renderCreateConnection(

‎packages/graph-explorer/src/modules/CreateConnection/CreateConnection.tsx‎

Lines changed: 86 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,15 @@ import type {
66

77
import { useQueryClient } from "@tanstack/react-query";
88
import { useAtomCallback } from "jotai/utils";
9+
import { ChevronRightIcon } from "lucide-react";
910
import { useCallback, useState } from "react";
1011

1112
import {
1213
Button,
1314
Checkbox,
15+
Collapsible,
16+
CollapsibleContent,
17+
CollapsibleTrigger,
1418
FormItem,
1519
InfoTooltip,
1620
InputField,
@@ -90,6 +94,15 @@ function mapToConnection(data: Required<ConnectionForm>): ConnectionConfig {
9094
};
9195
}
9296

97+
/**
98+
* Whether the advanced disclosure should start open. A connection that already
99+
* overrides one of these settings would otherwise hide that fact behind a
100+
* collapsed section, so editing it looks like the defaults are in force.
101+
*/
102+
function hasAdvancedOverrides(form: ConnectionForm): boolean {
103+
return form.fetchTimeoutEnabled || form.nodeExpansionLimitEnabled;
104+
}
105+
93106
/**
94107
* Maps a connection into form values under the given name. The caller picks the
95108
* name because only it knows the fallback: a stored connection falls back to
@@ -395,66 +408,79 @@ const CreateConnection = ({
395408
</FormItem>
396409
</>
397410
)}
398-
<FormItem>
399-
<Label className="cursor-pointer">
400-
<Checkbox
401-
value="fetchTimeoutEnabled"
402-
checked={form.fetchTimeoutEnabled}
403-
onCheckedChange={checked => {
404-
onFormChange("fetchTimeoutEnabled")(checked);
405-
}}
406-
/>
407-
<span className="flex items-center gap-2">
408-
Enable Fetch Timeout
409-
<InfoTooltip>
410-
Large datasets may require a large amount of time to fetch. If
411-
the timeout is exceeded, the request will be cancelled.
412-
</InfoTooltip>
413-
</span>
414-
</Label>
415-
</FormItem>
416-
{form.fetchTimeoutEnabled && (
417-
<FormItem>
418-
<Label>Fetch Timeout (ms)</Label>
419-
<InputField
420-
aria-label="Fetch Timeout (ms)"
421-
type="number"
422-
value={form.fetchTimeoutMs}
423-
onChange={onFormChange("fetchTimeoutMs")}
424-
min={0}
425-
/>
426-
</FormItem>
427-
)}
428-
<FormItem>
429-
<Label className="cursor-pointer">
430-
<Checkbox
431-
value="nodeExpansionLimitEnabled"
432-
checked={form.nodeExpansionLimitEnabled}
433-
onCheckedChange={checked => {
434-
onFormChange("nodeExpansionLimitEnabled")(checked);
435-
}}
436-
/>
437-
<span className="flex items-center gap-2">
438-
Override Default Neighbor Expansion Limit
439-
<InfoTooltip>
440-
Large datasets may require a default limit to the amount of
441-
neighbors that are returned during any single expansion.
442-
</InfoTooltip>
443-
</span>
444-
</Label>
445-
</FormItem>
446-
{form.nodeExpansionLimitEnabled && (
447-
<FormItem>
448-
<Label>Neighbor Expansion Limit</Label>
449-
<InputField
450-
aria-label="Neighbor Expansion Limit"
451-
type="number"
452-
value={form.nodeExpansionLimit}
453-
onChange={onFormChange("nodeExpansionLimit")}
454-
min={0}
455-
/>
456-
</FormItem>
457-
)}
411+
<Collapsible
412+
defaultOpen={hasAdvancedOverrides(form)}
413+
className="group flex flex-col gap-6"
414+
>
415+
{/* Renders its own button rather than `asChild` onto a div, so the
416+
disclosure stays keyboard operable and announces its expanded state. */}
417+
<CollapsibleTrigger className="group/advanced-trigger focus-visible:ring-primary/50 text-foreground flex w-fit cursor-pointer flex-row items-center gap-2 rounded-md text-sm leading-tight font-medium focus-visible:ring-[3px] focus-visible:outline-hidden">
418+
<ChevronRightIcon className="text-muted-foreground size-5 shrink-0 transition-transform duration-200 ease-in-out group-data-open/advanced-trigger:rotate-90" />
419+
Advanced options
420+
</CollapsibleTrigger>
421+
<CollapsibleContent className="flex flex-col gap-6">
422+
<FormItem>
423+
<Label className="cursor-pointer">
424+
<Checkbox
425+
value="fetchTimeoutEnabled"
426+
checked={form.fetchTimeoutEnabled}
427+
onCheckedChange={checked => {
428+
onFormChange("fetchTimeoutEnabled")(checked);
429+
}}
430+
/>
431+
<span className="flex items-center gap-2">
432+
Enable Fetch Timeout
433+
<InfoTooltip>
434+
Large datasets may require a large amount of time to fetch.
435+
If the timeout is exceeded, the request will be cancelled.
436+
</InfoTooltip>
437+
</span>
438+
</Label>
439+
</FormItem>
440+
{form.fetchTimeoutEnabled && (
441+
<FormItem>
442+
<Label>Fetch Timeout (ms)</Label>
443+
<InputField
444+
aria-label="Fetch Timeout (ms)"
445+
type="number"
446+
value={form.fetchTimeoutMs}
447+
onChange={onFormChange("fetchTimeoutMs")}
448+
min={0}
449+
/>
450+
</FormItem>
451+
)}
452+
<FormItem>
453+
<Label className="cursor-pointer">
454+
<Checkbox
455+
value="nodeExpansionLimitEnabled"
456+
checked={form.nodeExpansionLimitEnabled}
457+
onCheckedChange={checked => {
458+
onFormChange("nodeExpansionLimitEnabled")(checked);
459+
}}
460+
/>
461+
<span className="flex items-center gap-2">
462+
Override Default Neighbor Expansion Limit
463+
<InfoTooltip>
464+
Large datasets may require a default limit to the amount of
465+
neighbors that are returned during any single expansion.
466+
</InfoTooltip>
467+
</span>
468+
</Label>
469+
</FormItem>
470+
{form.nodeExpansionLimitEnabled && (
471+
<FormItem>
472+
<Label>Neighbor Expansion Limit</Label>
473+
<InputField
474+
aria-label="Neighbor Expansion Limit"
475+
type="number"
476+
value={form.nodeExpansionLimit}
477+
onChange={onFormChange("nodeExpansionLimit")}
478+
min={0}
479+
/>
480+
</FormItem>
481+
)}
482+
</CollapsibleContent>
483+
</Collapsible>
458484
</DialogBody>
459485
<DialogFooter>
460486
<Button variant="outline" onClick={onClose}>

‎packages/graph-explorer/src/utils/createDisplayError.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ describe("createDisplayError", () => {
237237
expect(result).toStrictEqual({
238238
title: "Fetch timeout exceeded",
239239
message:
240-
"The request did not finish within this connection's fetch timeout of 240,000 ms. Increase the Fetch Timeout in the connection's settings, or retry the request.",
240+
"The request did not finish within this connection's fetch timeout of 240,000 ms. Increase the Fetch Timeout in this connection's advanced options, or retry the request.",
241241
});
242242
});
243243

‎packages/graph-explorer/src/utils/createDisplayError.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ export function createDisplayError(error: any): DisplayError {
104104
if (error instanceof FetchTimeoutError) {
105105
return {
106106
title: "Fetch timeout exceeded",
107-
message: `The request did not finish within this connection's fetch timeout of ${error.timeoutMs.toLocaleString()} ms. Increase the Fetch Timeout in the connection's settings, or retry the request.`,
107+
message: `The request did not finish within this connection's fetch timeout of ${error.timeoutMs.toLocaleString()} ms. Increase the Fetch Timeout in this connection's advanced options, or retry the request.`,
108108
};
109109
}
110110

0 commit comments

Comments
 (0)