@craftercms/studio-ui
Version:
Services, components, models & utils to build CrafterCMS authoring extensions.
187 lines (185 loc) • 6.51 kB
JavaScript
/*
* Copyright (C) 2007-2022 Crafter Software Corporation. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Copyright (C) 2007-2022 Crafter Software Corporation. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React, { useEffect, useMemo, useState } from 'react';
import GraphiQLComponent, { ToolbarButton } from 'graphiql';
import 'graphiql/graphiql.min.css';
import GraphiQLExplorer from 'graphiql-explorer';
import { buildClientSchema, getIntrospectionQuery } from 'graphql';
import GlobalAppToolbar from '../GlobalAppToolbar';
import { FormattedMessage } from 'react-intl';
import Box from '@mui/material/Box';
import { toQueryString } from '../../utils/object';
import useUpdateRefs from '../../hooks/useUpdateRefs';
import { isBlank } from '../../utils/string';
import { defaultQuery } from './utils';
import useEnv from '../../hooks/useEnv';
import useActiveSiteId from '../../hooks/useActiveSiteId';
function GraphiQL(props) {
const { guestBase } = useEnv();
const site = useActiveSiteId();
const {
storageKey = site,
showAppsButton,
embedded = false,
method = 'post',
url = `${guestBase}/api/1/site/graphql${site ? `?crafterSite=${site}` : ''}`,
onSubmittingAndOrPendingChange
} = props;
// We don't want to update the initialQuery.
// eslint-disable-next-line react-hooks/exhaustive-deps
const initialQuery = useMemo(() => {
var _a;
return (_a = window.localStorage.getItem(`${storageKey}graphiql:query`)) !== null && _a !== void 0
? _a
: defaultQuery;
}, []);
const [query, setQuery] = useState(initialQuery);
const [schema, setSchema] = useState(null);
const [explorerIsOpen, setExplorerIsOpen] = useState(false);
const storage = useMemo(
() => ({
setItem: (key, value) => window.localStorage.setItem(`${storageKey}${key}`, value),
getItem: (key) => window.localStorage.getItem(`${storageKey}${key}`),
removeItem: (key) => window.localStorage.removeItem(`${storageKey}${key}`)
}),
[storageKey]
);
const graphQLFetcher = useMemo(() => {
const lowercaseMethod = method.toLowerCase();
const then = (response) => {
try {
return response.json();
} catch (error) {
return response;
}
};
if (lowercaseMethod === 'get') {
return (graphQLParams, opts) => {
var _a;
return fetch(
`${url}${toQueryString({
query: encodeURIComponent(graphQLParams.query),
variables: encodeURIComponent(
JSON.stringify((_a = graphQLParams.variables) !== null && _a !== void 0 ? _a : '{}')
)
})}`,
{
method: 'get',
headers: { 'Content-Type': 'application/json' }
}
).then(then);
};
} else {
return (graphQLParams, opts) => {
return fetch(url, {
method: 'post',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(graphQLParams)
}).then(then);
};
}
}, [url, method]);
const handleToggleExplorer = () => setExplorerIsOpen(!explorerIsOpen);
const refs = useUpdateRefs({ onSubmittingAndOrPendingChange, graphiql: null });
const hasChanges = isBlank(query) ? false : initialQuery !== query;
const onEditQuery = (newQuery) => {
setQuery(newQuery);
window.localStorage.setItem(`${storageKey}graphiql:query`, newQuery);
};
useEffect(() => {
var _a, _b;
(_b = (_a = refs.current).onSubmittingAndOrPendingChange) === null || _b === void 0
? void 0
: _b.call(_a, { hasPendingChanges: hasChanges });
}, [hasChanges, refs]);
useEffect(() => {
graphQLFetcher({
query: getIntrospectionQuery()
}).then((result) => {
setSchema(buildClientSchema(result.data));
});
}, [graphQLFetcher]);
return React.createElement(
Box,
{
sx: {
display: 'flex',
flexDirection: 'column',
height: '100%'
}
},
!embedded &&
React.createElement(GlobalAppToolbar, {
title: React.createElement(FormattedMessage, { id: 'words.graphQL', defaultMessage: 'GraphQL' }),
showAppsButton: showAppsButton
}),
React.createElement(
Box,
{
className: 'graphiql-container',
sx: {
height: 'calc(100% - 65px)',
position: 'relative',
'.title': {
display: 'none'
},
'.doc-explorer-title-bar, .history-title-bar': {
height: 'auto!important'
}
}
},
React.createElement(GraphiQLExplorer, {
schema: schema,
query: query,
onEdit: onEditQuery,
onRunOperation: (operationName) => refs.current.graphiql.handleRunQuery(operationName),
explorerIsOpen: explorerIsOpen,
onToggleExplorer: handleToggleExplorer
}),
React.createElement(GraphiQLComponent, {
ref: (ref) => (refs.current.graphiql = ref),
fetcher: graphQLFetcher,
schema: schema,
query: query,
storage: storage,
onEditQuery: onEditQuery,
toolbar: {
additionalContent: React.createElement(ToolbarButton, {
onClick: handleToggleExplorer,
label: 'Explorer',
title: 'Toggle Explorer'
})
}
})
)
);
}
export default GraphiQL;