UNPKG

@craftercms/studio-ui

Version:

Services, components, models & utils to build CrafterCMS authoring extensions.

187 lines (185 loc) 6.51 kB
/* * 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;