Skip to content

Commit 204fac7

Browse files
rohit-ravikotihuv1k
authored andcommitted
Schema polling autoupdate (#934)
* Merged from sdl-tabs https://github.com/Novvum/graphql-playground.git * Added and integrated setting's key for 'schema.disableComments' * Merged changes from disableComments branch * Fixes and style changes matching @kuldar's design specs. Details on #897 comments * Still not able to cmd+save settings. Might be a deeper issue. However, save button saves disabled comments * Added tabWidth prop to SideTab.tsx to provide specific widths for different tabs. * Fix for Tab Spacing in collapsed state Fix for additional line-breaks after each item * ugly version is done * moved icons to the left * making polling global * Updates for using 'esc' on keydown to close tabs * added polling config * Updated createSDL.tsx Schema will now default to true for commentsDisabled and commentDescription properties * Fixed Electron * Hiding reload icon if polling is enabled * * Updated schema polling icon * Schema only updates in state if it has changed * code cleanup * Merged from sdl-tabs https://github.com/Novvum/graphql-playground.git * Added and integrated setting's key for 'schema.disableComments' * Merged changes from disableComments branch * Fixes and style changes matching @kuldar's design specs. Details on #897 comments * Added tabWidth prop to SideTab.tsx to provide specific widths for different tabs. * Fix for Tab Spacing in collapsed state Fix for additional line-breaks after each item * ugly version is done * moved icons to the left * making polling global * Updates for using 'esc' on keydown to close tabs * added polling config * Updated createSDL.tsx Schema will now default to true for commentsDisabled and commentDescription properties * Fixed Electron * Hiding reload icon if polling is enabled * * Updated schema polling icon * Schema only updates in state if it has changed * code cleanup * removed questiomark * removed duplicate funciton * updated files from upstream * yarn.lock from upstream * using printSchema instead of JSON.stringify to compare schemas * reusing reload icon * "Refresh to see changes" feature in SCHEMA tab * disabled animation for polling * changed polling icon and moved back to right side. * automatically updating schema view without scrolling when schema updates * moved reload icon back to the left * automatically updating schema view without scrolling when schema updates * more accurate schema diff checking
1 parent 41fcd81 commit 204fac7

File tree

17 files changed

+242
-42
lines changed

17 files changed

+242
-42
lines changed

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

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import {
2727
} from '../state/sessions/actions'
2828
import { setConfigString } from '../state/general/actions'
2929
import { initState } from '../state/workspace/actions'
30-
import { GraphQLSchema } from 'graphql'
30+
import { GraphQLSchema, printSchema } from 'graphql'
3131
import { createStructuredSelector } from 'reselect'
3232
import {
3333
getIsConfigTab,
@@ -143,7 +143,11 @@ export class Playground extends React.PureComponent<Props & ReduxProps, State> {
143143
if (props.schema) {
144144
return
145145
}
146-
if (this.mounted && this.state.schema) {
146+
if (
147+
this.mounted &&
148+
this.state.schema &&
149+
!props.settings['schema.enablePolling']
150+
) {
147151
this.setState({ schema: undefined })
148152
}
149153
let first = true
@@ -243,6 +247,7 @@ export class Playground extends React.PureComponent<Props & ReduxProps, State> {
243247
async schemaGetter(propsInput?: Props & ReduxProps) {
244248
const props = this.props || propsInput
245249
const endpoint = props.sessionEndpoint || props.endpoint
250+
const currentSchema = this.state.schema
246251
try {
247252
const data = {
248253
endpoint,
@@ -258,11 +263,11 @@ export class Playground extends React.PureComponent<Props & ReduxProps, State> {
258263
data.endpoint === this.props.endpoint ||
259264
data.endpoint === this.props.sessionEndpoint
260265
) {
261-
this.setState({ schema: newSchema })
266+
this.updateSchema(currentSchema, newSchema, props)
262267
}
263268
})
264269
if (schema) {
265-
this.setState({ schema: schema.schema })
270+
this.updateSchema(currentSchema, schema.schema, props)
266271
this.props.schemaFetchingSuccess(data.endpoint, schema.tracingSupported)
267272
this.backoff.stop()
268273
}
@@ -349,6 +354,22 @@ export class Playground extends React.PureComponent<Props & ReduxProps, State> {
349354
)
350355
}
351356

357+
private updateSchema(
358+
currentSchema: GraphQLSchema | undefined,
359+
newSchema: GraphQLSchema,
360+
props: Readonly<{ children?: React.ReactNode }> &
361+
Readonly<Props & ReduxProps>,
362+
) {
363+
const currentSchemaStr = currentSchema ? printSchema(currentSchema) : null
364+
const newSchemaStr = printSchema(newSchema)
365+
if (
366+
newSchemaStr !== currentSchemaStr ||
367+
!props.settings['schema.enablePolling']
368+
) {
369+
this.setState({ schema: newSchema })
370+
}
371+
}
372+
352373
get httpApiPrefix() {
353374
return this.props.endpoint.match(/(https?:\/\/.*?)\/?/)![1]
354375
}

packages/graphql-playground-react/src/components/Playground/SchemaExplorer/SDLEditor.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as React from 'react'
2-
import { GraphQLSchema } from 'graphql'
2+
import { GraphQLSchema, printSchema } from 'graphql'
33
import EditorWrapper from '../EditorWrapper'
44
import { styled } from '../../../styled'
55
import { getSDL } from '../util/createSDL'
@@ -64,10 +64,12 @@ class SDLEditor extends React.PureComponent<Props, { overflowY: boolean }> {
6464
this.editor.on('scroll', this.handleScroll)
6565
this.editor.refresh()
6666
}
67-
6867
componentDidUpdate(prevProps: Props) {
6968
const CodeMirror = require('codemirror')
70-
if (this.props.schema !== prevProps.schema) {
69+
const currentSchemaStr = this.props.schema && printSchema(this.props.schema)
70+
const prevSchemaStr = prevProps.schema && printSchema(prevProps.schema)
71+
if (currentSchemaStr !== prevSchemaStr) {
72+
const initialScroll = this.editor.getScrollInfo()
7173
this.cachedValue =
7274
getSDL(
7375
this.props.schema,
@@ -79,6 +81,9 @@ class SDLEditor extends React.PureComponent<Props, { overflowY: boolean }> {
7981
this.props.settings['schema.disableComments'],
8082
),
8183
)
84+
if (this.props.settings['schema.enablePolling']) {
85+
this.editor.scrollTo(initialScroll.left, initialScroll.top)
86+
}
8287
CodeMirror.signal(this.editor, 'change', this.editor)
8388
}
8489
if (this.props.width !== prevProps.width) {

packages/graphql-playground-react/src/components/Playground/SchemaExplorer/SDLHeader.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,12 @@ export default class SDLHeader extends React.Component<SDLHeaderProps, State> {
8080

8181
const SchemaHeader = styled.div`
8282
display: flex;
83+
flex-direction: row;
8384
height: 64px;
8485
width: 100%;
86+
margin-right: 108px;
8587
align-items: center;
86-
justify-content: space-between;
88+
justify-content: flex-start;
8789
outline: none;
8890
user-select: none;
8991
`
@@ -99,7 +101,6 @@ const Box = styled.div`
99101
`
100102

101103
const Title = styled.div`
102-
flex: 1;
103104
color: ${p => styleHelper(p).title};
104105
cursor: default;
105106
font-size: 14px;
@@ -109,6 +110,7 @@ const Title = styled.div`
109110
letter-spacing: 1px;
110111
user-select: none !important;
111112
padding: 16px;
113+
padding-right: 5px;
112114
`
113115

114116
const Download = styled(Button)`
@@ -136,6 +138,7 @@ const styleHelper = p => {
136138
if (p.theme.mode === 'dark') {
137139
return {
138140
title: 'white',
141+
subtitle: '#8B959C',
139142
download: {
140143
text: p.open ? '#8B959C' : 'white',
141144
button: p.theme.colours.darkBlue,
@@ -148,6 +151,7 @@ const styleHelper = p => {
148151
}
149152
return {
150153
title: p.theme.colours.darkBlue,
154+
subtitle: 'rgba(61, 88, 102, 0.5)',
151155
download: {
152156
text: p.open ? 'rgba(61, 88, 102, 0.5)' : p.theme.colours.darkBlue,
153157
button: '#f6f6f6',

packages/graphql-playground-react/src/components/Playground/SchemaExplorer/SDLView.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ interface DispatchFromProps {
3232
toggleDocs: (sessionId: string) => any
3333
setDocsVisible: (sessionId: string, open: boolean) => any
3434
changeWidthDocs: (sessionId: string, width: number) => any
35+
setSchemaUpdated: () => void
3536
}
3637

3738
class SDLView extends React.Component<
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import * as React from 'react'
2+
import PollingIcon from './PollingIcon'
3+
4+
export interface Props {
5+
isPollingSchema: boolean
6+
onReloadSchema: () => void
7+
}
8+
9+
class SchemaPolling extends React.Component<Props> {
10+
timer: any
11+
12+
componentDidMount() {
13+
this.startPolling()
14+
}
15+
componentWillUnmount() {
16+
this.clearTimer()
17+
}
18+
componentWillReceiveProps(nextProps: Props) {
19+
if (nextProps.isPollingSchema !== this.props.isPollingSchema) {
20+
this.startPolling(nextProps)
21+
}
22+
}
23+
24+
render() {
25+
return <PollingIcon animate={true} />
26+
}
27+
private startPolling(props: Props = this.props) {
28+
this.clearTimer()
29+
if (props.isPollingSchema) {
30+
this.timer = setInterval(() => props.onReloadSchema(), 2000)
31+
}
32+
}
33+
34+
private clearTimer() {
35+
if (this.timer) {
36+
clearInterval(this.timer)
37+
this.timer = null
38+
}
39+
}
40+
}
41+
42+
export default SchemaPolling
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import * as React from 'react'
2+
import { styled, keyframes, css } from '../../../styled/index'
3+
import BasePositioner from './Positioner'
4+
5+
export interface Props {
6+
animate: boolean
7+
disabled?: boolean
8+
onClick?: () => void
9+
}
10+
11+
const PollingIcon: React.SFC<Props> = props => (
12+
<Positioner onClick={props.onClick} title="Polling Schema">
13+
<Icon animate={props.animate} />
14+
</Positioner>
15+
)
16+
17+
export default PollingIcon
18+
19+
const pulse = keyframes`
20+
0% {
21+
box-shadow: 0 0 0 0 rgba(139, 149, 156, 0.4);
22+
}
23+
70% {
24+
box-shadow: 0 0 0 10px rgba(139, 149, 156, 0);
25+
}
26+
100% {
27+
box-shadow: 0 0 0 0 rgba(139, 149, 156, 0);
28+
}
29+
`
30+
31+
const Positioner = styled(BasePositioner)`
32+
display: flex;
33+
justify-content: center;
34+
align-items: center;
35+
`
36+
const Icon = styled.div`
37+
display: block;
38+
width: 8px;
39+
height: 8px;
40+
border-radius: 50%;
41+
background: ${p => p.theme.editorColours.pollingIcon};
42+
box-shadow: 0 0 0 ${p => p.theme.editorColours.pollingIconShadow};
43+
${p =>
44+
p.animate
45+
? css`
46+
animation: ${pulse} 2s infinite;
47+
`
48+
: undefined};
49+
`
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { styled } from '../../../styled/index'
2+
3+
export default styled.div`
4+
width: 20px;
5+
height: 20px;
6+
`
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import * as React from 'react'
2+
import ReloadIcon from './ReloadIcon'
3+
4+
export interface Props {
5+
isReloadingSchema: boolean
6+
onReloadSchema?: () => void
7+
}
8+
9+
const Reload: React.SFC<Props> = props => (
10+
<ReloadIcon
11+
animate={props.isReloadingSchema}
12+
onClick={props.onReloadSchema}
13+
/>
14+
)
15+
16+
export default Reload
Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,32 @@
11
import * as React from 'react'
2-
import { styled, keyframes } from '../../../styled/index'
2+
import { styled, keyframes, css } from '../../../styled/index'
3+
import BasePositioner from './Positioner'
34

45
export interface Props {
5-
isReloadingSchema: boolean
6-
onReloadSchema?: () => void
6+
animate: boolean
7+
disabled?: boolean
8+
onClick?: () => void
79
}
810

911
const ReloadIcon: React.SFC<Props> = props => (
10-
<Positioner onClick={props.onReloadSchema} title="Reload Schema">
11-
<Svg viewBox="0 0 20 20">
12+
<Positioner
13+
onClick={props.onClick}
14+
title="Reload Schema"
15+
disabled={props.disabled}
16+
>
17+
<Svg viewBox="0 0 20 20" disabled={props.disabled}>
1218
<Circle
1319
cx="9.5"
1420
cy="10"
1521
r="6"
1622
strokeWidth="1.5"
1723
fill="none"
1824
strokeLinecap="round"
19-
isReloadingSchema={props.isReloadingSchema}
25+
animate={props.animate}
2026
/>
2127
<Icon
2228
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"
23-
isReloadingSchema={props.isReloadingSchema}
29+
animate={props.animate}
2430
/>
2531
</Svg>
2632
</Positioner>
@@ -50,45 +56,42 @@ const refreshFrames = keyframes`
5056
// again the animation
5157
const reloadAction = props => keyframes`
5258
0% {
53-
transform: rotate(${props.isReloadingSchema ? 0 : 360}deg);
59+
transform: rotate(${props.animate ? 0 : 360}deg);
5460
}
5561
5662
100% {
57-
transform: rotate(${props.isReloadingSchema ? 360 : 720}deg);
63+
transform: rotate(${props.animate ? 360 : 720}deg);
5864
}`
5965

60-
const Positioner = styled.div`
61-
position: absolute;
62-
right: 5px;
63-
width: 20px;
64-
height: 20px;
65-
cursor: pointer;
66-
transform: rotateY(180deg);
67-
`
68-
6966
const Svg = styled.svg`
7067
fill: ${p => p.theme.editorColours.icon};
7168
transition: 0.1s linear all;
72-
73-
&:hover {
74-
fill: ${p => p.theme.editorColours.iconHover};
75-
}
69+
${p =>
70+
p.disabled
71+
? undefined
72+
: css`
73+
&:hover {
74+
fill: ${p => p.theme.editorColours.iconHover};
75+
}
76+
`};
77+
`
78+
const Positioner = styled(BasePositioner)`
79+
cursor: ${({ disabled = false }) => (disabled ? 'auto' : 'pointer')};
80+
transform: rotateY(180deg);
7681
`
77-
7882
const Circle = styled<Props, 'circle'>('circle')`
7983
fill: none;
8084
stroke: ${p => p.theme.editorColours.icon};
8185
stroke-dasharray: 37.68;
8286
transition: opacity 0.3s ease-in-out;
83-
opacity: ${p => (p.isReloadingSchema ? 1 : 0)};
87+
opacity: ${p => (p.animate ? 1 : 0)};
8488
transform-origin: 9.5px 10px;
85-
animation: ${refreshFrames} 2s linear
86-
${p => (p.isReloadingSchema ? 'infinite' : '')};
89+
animation: ${refreshFrames} 2s linear ${p => (p.animate ? 'infinite' : '')};
8790
`
8891

8992
const Icon = styled<Props, 'path'>('path')`
9093
transition: opacity 0.3s ease-in-out;
91-
opacity: ${p => (p.isReloadingSchema ? 0 : 1)};
94+
opacity: ${p => (p.animate ? 0 : 1)};
9295
transform-origin: 9.5px 10px;
9396
animation: ${reloadAction} 0.5s linear;
9497
`
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import * as React from 'react'
2+
import ReloadIcon from './Reload'
3+
import PollingIcon from './Polling'
4+
5+
export interface Props {
6+
isPollingSchema: boolean
7+
isReloadingSchema: boolean
8+
onReloadSchema: () => any
9+
}
10+
11+
export default (props: Props) => {
12+
if (props.isPollingSchema) {
13+
return (
14+
<PollingIcon
15+
isPollingSchema={props.isPollingSchema}
16+
onReloadSchema={props.onReloadSchema}
17+
/>
18+
)
19+
}
20+
return (
21+
<ReloadIcon
22+
isReloadingSchema={props.isReloadingSchema}
23+
onReloadSchema={props.onReloadSchema}
24+
/>
25+
)
26+
}

0 commit comments

Comments
 (0)