diff --git a/src/CodeExporter.js b/src/CodeExporter.js index 47d4bc1..d249244 100644 --- a/src/CodeExporter.js +++ b/src/CodeExporter.js @@ -323,6 +323,13 @@ class CodeDisplay extends React.PureComponent { } } +export type PreGenerateCodesandboxOptions = { + query: string, + operationDataList: Array, + options: GenerateOptions, + snippet: Snippet, +}; + type Props = {| snippet: ?Snippet, snippets: Array, @@ -337,6 +344,8 @@ type Props = {| onSelectSnippet: ?(snippet: Snippet) => void, onSetOptionValue: ?(snippet: Snippet, option: string, value: boolean) => void, onGenerateCodesandbox?: ?({sandboxId: string}) => void, + preGenerateCodesandbox?: ?(PreGenerateCodesandboxOptions) => boolean, + codeSandboxButtonText?: ?string, schema: ?GraphQLSchema, |}; type State = {| @@ -399,7 +408,12 @@ class CodeExporter extends Component { }; }; - _generateCodesandbox = async (operationDataList: Array) => { + _generateCodesandbox = async ( + query, + operationDataList: Array, + ) => { + let shouldContinue = true; + this.setState({codesandboxResult: {type: 'loading'}}); const snippet = this._activeSnippet(); if (!snippet) { @@ -420,12 +434,30 @@ class CodeExporter extends Component { }); return; } + try { - const sandboxResult = await createCodesandbox( - generateFiles( - this._collectOptions(snippet, operationDataList, this.props.schema), - ), + const options = this._collectOptions( + snippet, + operationDataList, + this.props.schema, ); + + if (this.props.preGenerateCodesandbox) { + const opts: PreGenerateCodesandboxOptions = { + query: query, + operationDataList: operationDataList, + options: options, + snippet: snippet, + }; + shouldContinue = this.props.preGenerateCodesandbox(opts); + } + + if (!shouldContinue) { + console.debug('CodeSandbox generation stopped by parent component'); + return; + } + + const sandboxResult = await createCodesandbox(generateFiles(options)); this.setState({ codesandboxResult: {type: 'success', ...sandboxResult}, }); @@ -459,15 +491,15 @@ class CodeExporter extends Component { }; }; - render() { - const {query, snippets, variables = {}} = this.props; - const {showCopiedTooltip, codesandboxResult} = this.state; - - const snippet = this._activeSnippet(); + _computeOperationDataList({ + query, + variables, + }: { + query: string, + variables: Variables, + }) { const operationDefinitions = getOperationNodes(query); - const {name, language, generate} = snippet; - const fragmentDefinitions: Array = []; for (const operationDefinition of operationDefinitions) { @@ -497,6 +529,27 @@ class CodeExporter extends Component { const operationDataList = toposort(rawOperationDataList); + return { + operationDefinitions: operationDefinitions, + fragmentDefinitions: fragmentDefinitions, + rawOperationDataList: rawOperationDataList, + operationDataList: operationDataList, + }; + } + + render() { + const {query, snippets, variables = {}} = this.props; + const {showCopiedTooltip, codesandboxResult} = this.state; + + const snippet = this._activeSnippet(); + + const {name, language, generate} = snippet; + + const { + operationDefinitions, + operationDataList, + } = this._computeOperationDataList({query, variables}); + const optionValues: Array = this.getOptionValues(snippet); const codeSnippet = operationDefinitions.length @@ -578,7 +631,7 @@ class CodeExporter extends Component { {supportsCodesandbox ? (
{codesandboxResult ? (
@@ -609,9 +666,7 @@ class CodeExporter extends Component { + href={`https://codesandbox.io/s/${codesandboxResult.sandboxId}`}> Visit CodeSandbox )} @@ -731,79 +786,110 @@ type WrapperProps = { onSetOptionValue?: (snippet: Snippet, option: string, value: boolean) => void, optionValues?: OptionValues, onGenerateCodesandbox?: ?({sandboxId: string}) => void, + preGenerateCodesandbox?: ?() => boolean, schema: ?GraphQLSchema, + codeSandboxButtonText?: ?string, }; // we borrow class names from graphiql's CSS as the visual appearance is the same // yet we might want to change that at some point in order to have a self-contained standalone -export default function CodeExporterWrapper({ - query, - serverUrl, - variables, - context = {}, - headers = {}, - hideCodeExporter = () => {}, - snippets, - snippet, - codeMirrorTheme, - onSelectSnippet, - onSetOptionValue, - optionValues, - onGenerateCodesandbox, - schema, -}: WrapperProps) { - let jsonVariables: Variables = {}; - - try { - const parsedVariables = JSON.parse(variables); - if (typeof parsedVariables === 'object') { - jsonVariables = parsedVariables; +export default class CodeExporterWrapper extends Component { + codeExporter: ?CodeExporter; + + generateCodesandboxFiles({ + query, + variables, + }: { + query: string, + variables: Variables, + }) { + if (this.codeExporter) { + const {operationDataList} = this.codeExporter._computeOperationDataList({ + query, + variables, + }); + this.codeExporter && + this.codeExporter._generateCodesandbox(query, operationDataList); } - } catch (e) {} + } - return ( -
-
-
Code Exporter
-
-
- {'\u2715'} -
-
-
+ render() { + const { + query, + serverUrl, + variables, + context = {}, + headers = {}, + hideCodeExporter = () => {}, + snippets, + snippet, + codeMirrorTheme, + onSelectSnippet, + onSetOptionValue, + optionValues, + onGenerateCodesandbox, + preGenerateCodesandbox, + schema, + codeSandboxButtonText, + }: WrapperProps = this.props; + let jsonVariables: Variables = {}; + + try { + const parsedVariables = JSON.parse(variables); + if (typeof parsedVariables === 'object') { + jsonVariables = parsedVariables; + } + } catch (e) {} + + return (
- {snippets.length ? ( - - - - ) : ( -
- Please provide a list of snippets + className="docExplorerWrap" + style={{ + width: 440, + minWidth: 440, + zIndex: 7, + }}> +
+
Code Exporter
+
+
+ {'\u2715'} +
- )} +
+
+ {snippets.length ? ( + + { + this.codeExporter = ref; + }} + /> + + ) : ( +
+ Please provide a list of snippets +
+ )} +
-
- ); + ); + } } diff --git a/src/toposort.js b/src/toposort.js index 882e0fc..4cae898 100644 --- a/src/toposort.js +++ b/src/toposort.js @@ -1,5 +1,4 @@ // @flow -import type {FragmentDefinitionNode, OperationDefinitionNode} from 'graphql'; import type {OperationData} from './CodeExporter.js'; type stringBoolMap = {[string]: boolean};