I think we should approach this from the perspective of two discrete packages: graphiql
and graphql-ide
. Doing this allows us to design different APIs that cater to the distinct sets of users would be interested in using graphiql
directly. The break down of the two libraries is outlined below with motivations further down.
The actual names and API here couldn't be further from final. They were just what came to mind first.
This library is meant for simple users who have already bought into the GraphiQL experience and are looking to customize it. These customizations would include things like a custom fetcher
, themes, etc. They explicitly do not include any modification of the internal UI. If the user cares about modifying the internals of the UI they should go to graphql-ide
. If/when plugins and presets exist, they are implemented here - not in graphql-ide
.
import GraphiQL from "graphiql";
function fetcher(...args) {
// do something
}
const App = () => (
<GraphiQL fetcher={fetcher} prettyPrintResult={false} theme={myTheme} />
);
import GraphiQL from "graphiql";
import { css } from "glamor";
const App = () => {
function fetcher(...args) {
// do something
}
return (
<FlexColumn>
{/* every element corresponds to an HTML element and can be styled with your favorite library */}
<GraphiQL
fetcher={fetcher}
{...css({
marginBottom: 20
})}
/>
<toolbar>
<div>awesome toolbar content that modifies the fetcher</div>
</toolbar>
</FlexColumn>
);
};
This library is designed for someone building a custom GraphQL IDE.
This first example is a bare bones graphql IDE with an editor, a place for the response, and a button to send the query. We'd also provide default implementations of things like the schema explorer but since this UI does not use them, they do not show up. In this example (assuming no default behaviors), the submit button is always enabled - even when the query has a validation error.
import { QueryEditor, Response, Container, SubmitButton } from "graphql-ide";
const App = () => (
<Container>
<FlexRow>
<SubmitButton />
</FlexRow>
<FlexRow>
<QueryEditor />
<Response />
</FlexRow>
</Container>
);
We would also provide some hooks for fancy users. Here is what it would look like to explicitly disable that submit button when the query isn't valid:
import {
QueryEditor,
Response,
Container,
SubmitButton,
useQueryState,
useSchema
} from "graphql-ide";
const App = () => {
// grab the current query from the context
const { valid: queryIsValid } = useQueryState();
return (
<>
<FlexRow>
<SubmitButton disabled={!queryIsValid} />
</FlexRow>
<FlexRow>
<QueryEditor />
<Response />
</FlexRow>
</>
);
};
export default props => (
<Container>
<App {...props} />
</Container>
);
You can see how someone would slowly build up a custom environment including as much as they wanted. Here's an example with the default schema explorer. Note, graphql-ide would not provide a Drawer
component, that's up to the user.
import {
QueryEditor,
Response,
Container,
SubmitButton,
SchemaExplorer,
useQueryState,
useSchema
} from "graphql-ide";
import { Drawer } from "my-favorite-component-library";
function App() {
// grab the current query from the context
const { valid: queryIsValid } = useQueryState();
// keep track of the drawer state
const [drawerOpen, setDrawerOpen] = useState(false);
return (
<>
<FlexRow>
<SubmitButton disabled={!queryIsValid} />
</FlexRow>
<FlexRow>
<QueryEditor />
<Response />
</FlexRow>
<Drawer open={drawerOpen} setOpen={setDrawerOpen}>
<SchemaExplorer />
</Drawer>
</>
);
}
export default () => (
<Container>
<App />
</Container>
);
Users could build a custom SchemaExplorer with hooks that graphql-ide
would provide:
import { useSchemaExplorer } from 'graphql-ide'
function CustomSchemaExplorer() {
// load the schema explorer utility
const schema = useSchemaExplorer()
// track the name of the type that we are looking at
const [name, setName] = useState(null)
// the type we are highlighting in the explorer
const type = schema.getType(name)
return (
<>
<h2>{type.name}</h1>
{type.fields.map(field => (
<div>
<h3>{field.name}</h3>
<button onClick={() => setName(field.type.name)}>{field.Type}</button>
</div>
))}
</>
)
}
fetcher
is probably a prop toSubmitButton
which handles updating the state internally
- Everyone component should correspond with an HTML element with props passed through so that users can style it however they want
- The current API provides ways to add components that are outside of graphiql's domain.
- look at how many components in the documentation refer to something "above graphiql" or "below graphiql"
- If a user wants to put a toolbar above graphiql, they should do it like
<FlexColumn><toolbar /><GraphiQL/></FlexColumn>
- Could differentiate the two users (see below) with
<GraphiQL> />
vs<GraphiQL.Result />
- You'd never see
<GraphiQL/>
in a codebase with a custom IDE
- You'd never see
- How would designers customize a GraphQL environment? What would they change about GraphiQL?
- rearrange UI
- hide pieces of UI
- replace pieces of UI with their own
- build UI that changes how different things work
- the Headers section in Playground
- What state does GraphiQL maintain?
- What is core to an IDE?
- the query
- just a string?
- Parsed string would be nice too but we can make the user parse it if they want to do something fancy
- don't want to be responsible for the performance of the parser
- "fancy" here is things like "know if the query has a syntax or validation error"
- the result
- loading state
- the JSON response
- the schema
- utilities for traversing this should be made available
- the query
- What is not core to GraphQL but part of the experience?
- The query that is stored in local storage
- The history
- Wether the query is valid or not (if the submit button is enabled)
- Wether the schema explorer is open or not
- What is core to an IDE?
- There are two kinds of users
- People who want to drop in a GraphiQL component so they can provide a custom fetcher (aka "simple")
- People who want to build custom IDEs for GraphiQL and can be expected to take more ownership over bits of state and what not (aka "advanced")