Skip to content

Commit 4423bac

Browse files
committed
Merge branch 'xavcz-feedback-on-reload-schema'
2 parents 640a492 + 4b5b75f commit 4423bac

File tree

3 files changed

+172
-48
lines changed

3 files changed

+172
-48
lines changed

packages/graphql-playground-react/src/components/Playground/GraphQLEditor.tsx

Lines changed: 46 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ export interface State {
123123
tracingSupported?: boolean
124124
queryVariablesActive: boolean
125125
endpointUnreachable: boolean
126+
isReloadingSchema: boolean
126127
}
127128

128129
export interface SimpleProps {
@@ -138,7 +139,7 @@ export interface ToolbarButtonProps extends SimpleProps {
138139
export class GraphQLEditor extends React.PureComponent<
139140
Props & LocalThemeInterface & ReduxProps,
140141
State
141-
> {
142+
> {
142143
static Logo: (props: SimpleProps) => JSX.Element
143144
static Toolbar: (props: SimpleProps) => JSX.Element
144145
static Footer: (props: SimpleProps) => JSX.Element
@@ -190,10 +191,10 @@ export class GraphQLEditor extends React.PureComponent<
190191
props.storage || typeof window !== 'undefined'
191192
? window.localStorage
192193
: {
193-
setItem: () => null,
194-
removeItem: () => null,
195-
getItem: () => null,
196-
}
194+
setItem: () => null,
195+
removeItem: () => null,
196+
getItem: () => null,
197+
}
197198

198199
// Determine the initial query to display.
199200
const query =
@@ -217,10 +218,10 @@ export class GraphQLEditor extends React.PureComponent<
217218
props.operationName !== undefined
218219
? props.operationName
219220
: getSelectedOperationName(
220-
null,
221-
this.storageGet('operationName'),
222-
queryFacts && queryFacts.operations,
223-
)
221+
null,
222+
this.storageGet('operationName'),
223+
queryFacts && queryFacts.operations,
224+
)
224225

225226
let queryVariablesActive = this.storageGet('queryVariablesActive')
226227
queryVariablesActive =
@@ -253,6 +254,7 @@ export class GraphQLEditor extends React.PureComponent<
253254
selectedVariableNames: [],
254255
queryVariablesActive,
255256
endpointUnreachable: false,
257+
isReloadingSchema: false,
256258
...queryFacts,
257259
}
258260

@@ -272,7 +274,7 @@ export class GraphQLEditor extends React.PureComponent<
272274

273275
// Utility for keeping CodeMirror correctly sized.
274276
this.codeMirrorSizer = new CodeMirrorSizer()
275-
;(global as any).g = this
277+
; (global as any).g = this
276278
}
277279

278280
componentWillReceiveProps(nextProps) {
@@ -503,6 +505,7 @@ export class GraphQLEditor extends React.PureComponent<
503505
onClickShare={this.props.onClickShare}
504506
sharing={this.props.sharing}
505507
onReloadSchema={this.reloadSchema}
508+
isReloadingSchema={this.state.isReloadingSchema}
506509
fixedEndpoint={this.props.fixedEndpoint}
507510
endpointUnreachable={this.state.endpointUnreachable}
508511
/>
@@ -564,13 +567,13 @@ export class GraphQLEditor extends React.PureComponent<
564567
onRunQuery={this.handleEditorRunQuery}
565568
/>
566569
) : (
567-
<VariableEditor
568-
ref={this.setVariableEditorComponent}
569-
value={this.props.session.headers}
570-
onEdit={this.props.onChangeHeaders}
571-
onRunQuery={this.handleEditorRunQuery}
572-
/>
573-
)}
570+
<VariableEditor
571+
ref={this.setVariableEditorComponent}
572+
value={this.props.session.headers}
573+
onEdit={this.props.onChangeHeaders}
574+
onRunQuery={this.handleEditorRunQuery}
575+
/>
576+
)}
574577
</div>
575578
<QueryDragBar ref={this.setQueryResizer} />
576579
</div>
@@ -667,7 +670,7 @@ export class GraphQLEditor extends React.PureComponent<
667670
.join(' ')
668671
return `curl '${
669672
this.props.session.endpoint
670-
}' ${headersString} --data-binary '${data}' --compressed`
673+
}' ${headersString} --data-binary '${data}' --compressed`
671674
}
672675

673676
setQueryVariablesRef = ref => {
@@ -757,14 +760,26 @@ export class GraphQLEditor extends React.PureComponent<
757760
// Private methods
758761

759762
public reloadSchema = async () => {
760-
const result = await this.props.schemaFetcher.refetch(
761-
this.props.session.endpoint || this.props.endpoint,
762-
this.convertHeaders(this.props.session.headers),
763-
)
764-
if (result) {
765-
const { schema } = result
766-
this.setState({ schema })
767-
this.renewStacks(schema)
763+
try {
764+
this.setState({ isReloadingSchema: true })
765+
const result = await this.props.schemaFetcher.refetch(
766+
this.props.session.endpoint || this.props.endpoint,
767+
this.convertHeaders(this.props.session.headers),
768+
)
769+
if (result) {
770+
const { schema } = result
771+
this.setState({
772+
schema,
773+
isReloadingSchema: false,
774+
endpointUnreachable: false,
775+
})
776+
this.renewStacks(schema)
777+
}
778+
} catch (e) {
779+
this.setState({
780+
isReloadingSchema: false,
781+
endpointUnreachable: true,
782+
})
768783
}
769784
}
770785

@@ -799,9 +814,9 @@ export class GraphQLEditor extends React.PureComponent<
799814

800815
this.props.schemaFetcher
801816
.fetch(
802-
this.props.session.endpoint || this.props.endpoint,
803-
this.convertHeaders(this.props.session.headers),
804-
)
817+
this.props.session.endpoint || this.props.endpoint,
818+
this.convertHeaders(this.props.session.headers),
819+
)
805820
.then(result => {
806821
if (result) {
807822
const { schema, tracingSupported } = result
@@ -1290,11 +1305,11 @@ const DragBar = styled.div`
12901305
cursor: col-resize;
12911306
`
12921307

1293-
const QueryDragBar = styled(DragBar)`
1308+
const QueryDragBar = styled(DragBar) `
12941309
right: 0px;
12951310
`
12961311

1297-
const ResultDragBar = styled(DragBar)`
1312+
const ResultDragBar = styled(DragBar) `
12981313
left: 0px;
12991314
z-index: 1;
13001315
`
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import * as React from 'react'
2+
import { styled, keyframes, withProps } from '../../../styled/index'
3+
import * as theme from 'styled-theming'
4+
5+
export interface Props {
6+
isReloadingSchema: boolean
7+
onReloadSchema?: () => void
8+
}
9+
10+
export default class ReloadIcon extends React.Component<Props, {}> {
11+
render() {
12+
return (
13+
<Positioner onClick={this.props.onReloadSchema}>
14+
<Svg viewBox="0 0 20 20">
15+
<Circle
16+
cx="9.5"
17+
cy="10"
18+
r="6"
19+
strokeWidth="1.5"
20+
fill="none"
21+
strokeLinecap="round"
22+
isReloadingSchema={this.props.isReloadingSchema}
23+
/>
24+
<Icon
25+
d="M4.83 4.86a6.92 6.92 0 0 1 11.3 2.97l.41-1.23c.13-.4.56-.6.95-.47.4.13.6.56.47.95l-1.13 3.33a.76.76 0 0 1-.7.5.72.72 0 0 1-.43-.12l-2.88-1.92a.76.76 0 0 1-.2-1.04.75.75 0 0 1 1.03-.2l1.06.7A5.34 5.34 0 0 0 9.75 4.5a5.44 5.44 0 0 0-5.64 5.22 5.42 5.42 0 0 0 5.24 5.62c.41 0 .74.36.72.78a.75.75 0 0 1-.75.72H9.3a6.9 6.9 0 0 1-6.68-7.18 6.88 6.88 0 0 1 2.22-4.81z"
26+
isReloadingSchema={this.props.isReloadingSchema}
27+
/>
28+
</Svg>
29+
</Positioner>
30+
)
31+
}
32+
}
33+
34+
const iconColor = theme('mode', {
35+
light: p => p.theme.colours.darkBlue20,
36+
dark: p => p.theme.colours.white20,
37+
})
38+
39+
const iconColorHover = theme('mode', {
40+
light: p => p.theme.colours.darkBlue60,
41+
dark: p => p.theme.colours.white60,
42+
})
43+
44+
const refreshFrames = keyframes`
45+
0% {
46+
transform: rotate(0deg);
47+
stroke-dashoffset: 7.92;
48+
}
49+
50+
50% {
51+
transform: rotate(720deg);
52+
stroke-dashoffset: 37.68;
53+
}
54+
55+
100% {
56+
transform: rotate(1080deg);
57+
stroke-dashoffset: 7.92;
58+
}
59+
`
60+
61+
// same result for these 2 keyframes, however when the props change
62+
// it makes the element animated with these keyframes to trigger
63+
// again the animation
64+
const reloadAction = props => keyframes`
65+
0% {
66+
transform: rotate(${props.isReloadingSchema ? 0 : 360}deg);
67+
}
68+
69+
100% {
70+
transform: rotate(${props.isReloadingSchema ? 360 : 720}deg);
71+
}`
72+
73+
const Positioner = styled.div`
74+
position: absolute;
75+
right: 5px;
76+
width: 20px;
77+
height: 20px;
78+
cursor: pointer;
79+
transform: rotateY(180deg);
80+
`
81+
82+
const Svg = styled.svg`
83+
fill: ${iconColor};
84+
transition: 0.1s linear all;
85+
86+
&:hover {
87+
fill: ${iconColorHover};
88+
}
89+
`
90+
91+
const Circle = withProps<Props>()(styled.circle) `
92+
fill: none;
93+
stroke: ${iconColor};
94+
stroke-dasharray: 37.68;
95+
transition: opacity 0.3s ease-in-out;
96+
opacity: ${p => p.isReloadingSchema ? 1 : 0};
97+
transform-origin: 9.5px 10px;
98+
animation: ${refreshFrames} 2s linear infinite;
99+
`
100+
101+
const Icon = withProps<Props>()(styled.path) `
102+
transition: opacity 0.3s ease-in-out;
103+
opacity: ${p => p.isReloadingSchema ? 0 : 1};
104+
transform-origin: 9.5px 10px;
105+
animation: ${reloadAction} 0.5s linear;
106+
`

packages/graphql-playground-react/src/components/Playground/TopBar/TopBar.tsx

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import * as theme from 'styled-theming'
44
import { darken, lighten } from 'polished'
55
import * as CopyToClipboard from 'react-copy-to-clipboard'
66
import Share, { SharingProps } from '../../Share'
7+
import ReloadIcon from './ReloadIcon'
8+
import { StyledComponentClass } from 'styled-components'
79
import { keyframes, StyledComponentClass } from 'styled-components'
810
import { Icon } from 'graphcool-styles'
911
import * as cx from 'classnames'
@@ -17,6 +19,7 @@ export interface Props {
1719
curl: string
1820
onClickShare?: () => void
1921
onReloadSchema?: () => void
22+
isReloadingSchema: boolean
2023
sharing?: SharingProps
2124
fixedEndpoint?: boolean
2225
endpointUnreachable: boolean
@@ -44,13 +47,11 @@ export default class TopBar extends React.Component<Props, {}> {
4447
<Spinner />
4548
</ReachError>
4649
) : (
47-
<ReloadIcon
48-
src={require('graphcool-styles/icons/fill/reload.svg')}
49-
width={20}
50-
height={20}
51-
onClick={this.props.onReloadSchema}
52-
/>
53-
)}
50+
<ReloadIcon
51+
isReloadingSchema={this.props.isReloadingSchema}
52+
onReloadSchema={this.props.onReloadSchema}
53+
/>
54+
)}
5455
</UrlBarWrapper>
5556
<CopyToClipboard text={this.props.curl}>
5657
<Button>Copy CURL</Button>
@@ -100,16 +101,6 @@ const fontColor = theme('mode', {
100101
dark: p => p.theme.colours.white60,
101102
})
102103

103-
const iconColor = theme('mode', {
104-
light: p => p.theme.colours.darkBlue20,
105-
dark: p => p.theme.colours.white20,
106-
})
107-
108-
const iconColorHover = theme('mode', {
109-
light: p => p.theme.colours.darkBlue60,
110-
dark: p => p.theme.colours.white60,
111-
})
112-
113104
const barBorder = theme('mode', {
114105
light: p => p.theme.colours.darkBlue20,
115106
dark: p => '#09141c',
@@ -176,6 +167,18 @@ const ReachError = styled.div`
176167
color: #f25c54;
177168
`
178169

170+
171+
const Spinner = styled.div`
172+
& {
173+
width: 40px;
174+
height: 40px;
175+
margin: 40px auto;
176+
background-color: #333;
177+
border-radius: 100%;
178+
-webkit-animation: sk-pulseScaleOut 1s infinite ease-in-out;
179+
animation: sk-pulseScaleOut 1s infinite ease-in-out;
180+
`
181+
179182
const ReloadIcon = styled(Icon)`
180183
position: absolute;
181184
right: 5px;

0 commit comments

Comments
 (0)