Skip to content

Commit d3ea7fb

Browse files
extract loading spinner to @graphiql/react (#2581)
1 parent 285bb99 commit d3ea7fb

File tree

8 files changed

+69
-54
lines changed

8 files changed

+69
-54
lines changed

.changeset/smooth-countries-shout.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@graphiql/react': minor
3+
---
4+
5+
Add `Spinner` component to `@graphiql/react`
+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export * from './button';
22
export * from './dropdown';
3+
export * from './spinner';
+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
.graphiql-spinner {
2+
height: 56px;
3+
margin: auto;
4+
margin-top: var(--px-16);
5+
width: 56px;
6+
7+
&::after {
8+
animation: rotation 0.8s linear 0s infinite;
9+
border: 4px solid transparent;
10+
border-radius: 100%;
11+
border-top: 4px solid var(--color-neutral-40);
12+
content: '';
13+
display: inline-block;
14+
height: 46px;
15+
vertical-align: middle;
16+
width: 46px;
17+
}
18+
}
19+
20+
@keyframes rotation {
21+
from {
22+
transform: rotate(0deg);
23+
}
24+
to {
25+
transform: rotate(360deg);
26+
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import './spinner.css';
2+
3+
export function Spinner() {
4+
return <div className="graphiql-spinner" />;
5+
}

packages/graphiql/__mocks__/@graphiql/react.tsx

+26-21
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export {
3838
SchemaContext,
3939
SchemaContextProvider,
4040
SettingsIcon,
41+
Spinner,
4142
StopIcon,
4243
StorageContext,
4344
StorageContextProvider,
@@ -53,7 +54,6 @@ export {
5354
useMergeQuery,
5455
usePrettifyEditors,
5556
useSchemaContext,
56-
useSelectHistoryItem,
5757
useStorageContext,
5858
} from '@graphiql/react';
5959

@@ -93,15 +93,20 @@ function useMockedEditor(
9393
onEdit?: (newValue: string) => void,
9494
) {
9595
const editorContext = useEditorContext({ nonNull: true });
96-
const [code, setCode] = useState(
97-
value ?? editorContext[NAME_TO_INITIAL_VALUE[name]],
98-
);
96+
const [code, setCode] = useState(() => {
97+
const initialValueProp = NAME_TO_INITIAL_VALUE[name];
98+
return (
99+
value ??
100+
(initialValueProp ? editorContext[initialValueProp] : undefined) ??
101+
''
102+
);
103+
});
99104
const ref = useRef<HTMLDivElement>(null);
100105

101106
const setEditor =
102107
editorContext[`set${name.slice(0, 1).toUpperCase()}${name.slice(1)}Editor`];
103108

104-
const getValueRef = useRef<() => string>();
109+
const getValueRef = useRef<() => string>(() => code);
105110
useEffect(() => {
106111
getValueRef.current = () => code;
107112
}, [code]);
@@ -180,28 +185,28 @@ function useMockedEditor(
180185
return ref;
181186
}
182187

183-
export const useHeaderEditor: typeof _useHeaderEditor = function useHeaderEditor({
184-
onEdit,
185-
}) {
186-
return useMockedEditor('header', undefined, onEdit);
188+
export const useHeaderEditor: typeof _useHeaderEditor = function useHeaderEditor(
189+
props,
190+
) {
191+
return useMockedEditor('header', undefined, props?.onEdit);
187192
};
188193

189-
export const useQueryEditor: typeof _useQueryEditor = function useQueryEditor({
190-
onEdit,
191-
}) {
192-
return useMockedEditor('query', undefined, onEdit);
194+
export const useQueryEditor: typeof _useQueryEditor = function useQueryEditor(
195+
props,
196+
) {
197+
return useMockedEditor('query', undefined, props?.onEdit);
193198
};
194199

195-
export const useResponseEditor: typeof _useResponseEditor = function useResponseEditor({
196-
value,
197-
}) {
198-
return useMockedEditor('response', value);
200+
export const useResponseEditor: typeof _useResponseEditor = function useResponseEditor(
201+
props,
202+
) {
203+
return useMockedEditor('response', props?.value);
199204
};
200205

201-
export const useVariableEditor: typeof _useVariableEditor = function useVariableEditor({
202-
onEdit,
203-
}) {
204-
return useMockedEditor('variable', undefined, onEdit);
206+
export const useVariableEditor: typeof _useVariableEditor = function useVariableEditor(
207+
props,
208+
) {
209+
return useMockedEditor('variable', undefined, props?.onEdit);
205210
};
206211

207212
export const HeaderEditor: typeof _HeaderEditor = function HeaderEditor(props) {

packages/graphiql/src/components/DocExplorer.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8-
import React, { ReactNode } from 'react';
8+
import { Spinner, useExplorerContext, useSchemaContext } from '@graphiql/react';
99
import { GraphQLSchema, isType } from 'graphql';
10-
import { useExplorerContext, useSchemaContext } from '@graphiql/react';
10+
import React, { ReactNode } from 'react';
1111

1212
import FieldDoc from './DocExplorer/FieldDoc';
1313
import SchemaDoc from './DocExplorer/SchemaDoc';
@@ -60,7 +60,7 @@ export function DocExplorer(props: DocExplorerProps) {
6060
);
6161
} else if (isFetching) {
6262
// Schema is undefined when it is being loaded via introspection.
63-
content = <div className="graphiql-spinner" />;
63+
content = <Spinner />;
6464
} else if (!schema) {
6565
// Schema is null when it explicitly does not exist, typically due to
6666
// an error during introspection.

packages/graphiql/src/components/GraphiQL.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import {
4343
ResponseEditor,
4444
SchemaContextProvider,
4545
SettingsIcon,
46+
Spinner,
4647
StorageContextProvider,
4748
ToolbarButton,
4849
UnStyledButton,
@@ -968,7 +969,7 @@ class GraphiQLWithContext extends React.Component<
968969
<div ref={this.props.editorResize.secondRef}>
969970
<div className="graphiql-response">
970971
{this.props.executionContext.isFetching ? (
971-
<div className="graphiql-spinner" />
972+
<Spinner />
972973
) : null}
973974
<ResponseEditor
974975
value={this.props.response}

packages/graphiql/src/style.css

-29
Original file line numberDiff line numberDiff line change
@@ -145,35 +145,6 @@
145145
padding: var(--px-16);
146146
}
147147

148-
/* Generic loading spinner */
149-
.graphiql-container .graphiql-spinner {
150-
height: 56px;
151-
left: 50%;
152-
position: relative;
153-
top: 50%;
154-
transform: translate(-50%, -50%);
155-
width: 56px;
156-
}
157-
.graphiql-container .graphiql-spinner::after {
158-
animation: rotation 0.6s infinite linear;
159-
border: 4px solid transparent;
160-
border-radius: 100%;
161-
border-top: 4px solid var(--color-neutral-40);
162-
content: '';
163-
display: inline-block;
164-
height: 46px;
165-
vertical-align: middle;
166-
width: 46px;
167-
}
168-
@keyframes rotation {
169-
from {
170-
transform: rotate(0deg);
171-
}
172-
to {
173-
transform: rotate(359deg);
174-
}
175-
}
176-
177148
/* Generic drag bar for horizontal resizing */
178149
.graphiql-container .graphiql-horizontal-drag-bar {
179150
width: var(--px-12);

0 commit comments

Comments
 (0)