Skip to content

Commit df03c92

Browse files
v6: Response pane polish (spinner overlay, consistent inset, table divider) (#4414)
## Summary Three small fixes to the response pane, grouped since they all touch the same surface: - **The loading spinner no longer shifts the layout.** It was rendered as a flex sibling above the response, so every query pushed the view picker and results down, then snapped back when the response arrived. It's now an absolutely-positioned overlay centered over the response body, so nothing reflows. It's also smaller and uses the accent color to match the header. - **Consistent inset across all three response views.** The JSON result window sat flush against the header, while Tree and Table used different insets (12/16px and 14px). All three now use the same `var(--px-16)` as the query and variables editors. - **Table view rows get their divider back.** The row border referenced `--border-subtle`, an undefined custom property, so per the CSS spec the whole declaration was dropped and rows rendered with no separator. It now uses `--border-muted`. ## Test plan - [x] Run a slow query. The spinner appears centered over the response area, and the JSON/Tree/Table view picker above it does not jump or reflow when the spinner appears or clears. - [x] With a result showing, confirm the JSON has an even 16px inset on all sides (matching the query editor), not flush to the header. Switch to Tree and Table views; the inset is the same in all three. - [x] Run a query returning a list and switch to Table view. Each row has a subtle bottom divider, consistent with the header separator. - [ ] `yarn workspace @graphiql/react test` and `yarn workspace graphiql test` pass. Refs: #4219
1 parent 1919f6a commit df03c92

6 files changed

Lines changed: 67 additions & 27 deletions

File tree

.changeset/response-pane-polish.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'graphiql': patch
3+
'@graphiql/react': patch
4+
---
5+
6+
Polish the response pane: the loading spinner is now a centered overlay, so the view picker and results no longer jump when a query runs; the JSON, Tree, and Table views share a consistent 16px inset; and Table view rows get their bottom divider back.

packages/graphiql-react/src/components/response-editor.tsx

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { ImagePreview } from './image-preview';
66
import { ResponseHeader } from './response-header';
77
import { ResponseTableView } from './response-table-view';
88
import { ResponseTreeView } from './response-tree-view';
9+
import { Spinner } from './spinner';
910
import {
1011
getOrCreateModel,
1112
createEditor,
@@ -48,6 +49,7 @@ export const ResponseEditor: FC<ResponseEditorProps> = ({
4849
validationErrors,
4950
responseEditor,
5051
uriInstanceId,
52+
isFetching,
5153
lastResponse,
5254
responseView,
5355
transport,
@@ -58,6 +60,7 @@ export const ResponseEditor: FC<ResponseEditorProps> = ({
5860
'validationErrors',
5961
'responseEditor',
6062
'uriInstanceId',
63+
'isFetching',
6164
'lastResponse',
6265
'responseView',
6366
'transport',
@@ -202,27 +205,32 @@ export const ResponseEditor: FC<ResponseEditorProps> = ({
202205
onCopy={handleCopy}
203206
/>
204207
)}
205-
<section
206-
ref={ref}
207-
aria-label="Result Window"
208-
aria-live="polite"
209-
aria-atomic="true"
210-
tabIndex={0}
211-
onKeyDown={onEditorContainerKeyDown}
212-
className="result-window"
213-
hidden={responseView !== 'json'}
214-
/>
215-
{responseView === 'tree' &&
216-
(lastResponse ? (
217-
<ResponseTreeView data={lastResponse.body} />
218-
) : (
219-
<div className="graphiql-response-empty-state" role="status">
220-
<span>Run a query to see the tree view.</span>
221-
</div>
222-
))}
223-
{responseView === 'table' && (
224-
<ResponseTableView data={lastResponse?.body} />
225-
)}
208+
<div className="graphiql-response-body">
209+
{isFetching && (
210+
<Spinner className="graphiql-response-spinner-overlay" />
211+
)}
212+
<section
213+
ref={ref}
214+
aria-label="Result Window"
215+
aria-live="polite"
216+
aria-atomic="true"
217+
tabIndex={0}
218+
onKeyDown={onEditorContainerKeyDown}
219+
className="result-window"
220+
hidden={responseView !== 'json'}
221+
/>
222+
{responseView === 'tree' &&
223+
(lastResponse ? (
224+
<ResponseTreeView data={lastResponse.body} />
225+
) : (
226+
<div className="graphiql-response-empty-state" role="status">
227+
<span>Run a query to see the tree view.</span>
228+
</div>
229+
))}
230+
{responseView === 'table' && (
231+
<ResponseTableView data={lastResponse?.body} />
232+
)}
233+
</div>
226234
</div>
227235
);
228236
};

packages/graphiql-react/src/components/response-header/index.css

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,38 @@
7070
overflow: hidden;
7171
}
7272

73+
/* Positions the loading spinner overlay below the header without taking up
74+
flex layout space, so it never shifts the header or result body. */
75+
.graphiql-response-body {
76+
display: flex;
77+
flex-direction: column;
78+
flex: 1;
79+
min-height: 0;
80+
position: relative;
81+
}
82+
7383
.graphiql-response-pane .result-window {
7484
flex: 1;
7585
min-height: 0;
86+
padding: var(--px-16);
87+
}
88+
89+
/* Smaller than the default .graphiql-spinner and tokenized to match the
90+
response header, since it floats over polished chrome rather than a bare
91+
loading screen. */
92+
.graphiql-response-spinner-overlay {
93+
position: absolute;
94+
inset: 0;
95+
margin: auto;
96+
height: 28px;
97+
width: 28px;
98+
99+
&::after {
100+
height: 22px;
101+
width: 22px;
102+
border-color: transparent;
103+
border-top-color: oklch(var(--accent-blue));
104+
}
76105
}
77106

78107
.graphiql-response-empty-state {

packages/graphiql-react/src/components/response-table-view/index.css

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
.graphiql-response-table-wrapper {
22
flex: 1;
33
overflow: auto;
4-
padding: var(--px-14);
4+
padding: var(--px-16);
55
}
66

77
.graphiql-response-table:not(:last-child) {
@@ -41,7 +41,7 @@
4141

4242
.graphiql-response-table td {
4343
padding: var(--px-6) var(--px-12);
44-
border-bottom: 1px solid oklch(var(--border-subtle));
44+
border-bottom: 1px solid oklch(var(--border-muted));
4545
vertical-align: top;
4646
max-width: 320px;
4747
overflow: hidden;

packages/graphiql-react/src/components/response-tree-view/index.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
font-size: var(--font-size-mono);
44
line-height: 1.6;
55
color: oklch(var(--fg-default));
6-
padding: var(--px-12) var(--px-16);
6+
padding: var(--px-16);
77
overflow: auto;
88
flex: 1;
99
min-height: 0;

packages/graphiql/src/GraphiQL.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,6 @@ export const GraphiQLInterface: FC<GraphiQLInterfaceProps> = ({
257257
initialHeaders,
258258
tabs,
259259
activeTabIndex,
260-
isFetching,
261260
visiblePlugin,
262261
operations,
263262
plugins,
@@ -269,7 +268,6 @@ export const GraphiQLInterface: FC<GraphiQLInterfaceProps> = ({
269268
'initialHeaders',
270269
'tabs',
271270
'activeTabIndex',
272-
'isFetching',
273271
'visiblePlugin',
274272
'operations',
275273
'plugins',
@@ -614,7 +612,6 @@ export const GraphiQLInterface: FC<GraphiQLInterfaceProps> = ({
614612
className="graphiql-response-column"
615613
>
616614
<div className="graphiql-response">
617-
{isFetching && <Spinner />}
618615
<ResponseEditor responseTooltip={responseTooltip} />
619616
{footer}
620617
</div>

0 commit comments

Comments
 (0)