+ );
+};
diff --git a/packages/graphiql-plugin-doc-explorer/src/components/type-documentation.tsx b/packages/graphiql-plugin-doc-explorer/src/components/type-documentation.tsx
index 219ecf3488b..b2d010de80d 100644
--- a/packages/graphiql-plugin-doc-explorer/src/components/type-documentation.tsx
+++ b/packages/graphiql-plugin-doc-explorer/src/components/type-documentation.tsx
@@ -24,16 +24,25 @@ type TypeDocumentationProps = {
* The type that should be rendered.
*/
type: GraphQLNamedType;
+ /**
+ * When true, the description, implements-interfaces, and fields sections
+ * are omitted. Used when TypeCard + FieldsList are already rendering those
+ * above the type documentation.
+ */
+ hideHeader?: boolean;
};
-export const TypeDocumentation: FC = ({ type }) => {
+export const TypeDocumentation: FC = ({
+ type,
+ hideHeader,
+}) => {
return isNamedType(type) ? (
<>
- {type.description ? (
+ {!hideHeader && type.description ? (
{type.description}
) : null}
-
-
+ {!hideHeader && }
+ {!hideHeader && }
>
diff --git a/packages/graphiql-plugin-doc-explorer/src/context.tsx b/packages/graphiql-plugin-doc-explorer/src/context.tsx
index e0bb2c97a8a..91e12425063 100644
--- a/packages/graphiql-plugin-doc-explorer/src/context.tsx
+++ b/packages/graphiql-plugin-doc-explorer/src/context.tsx
@@ -96,7 +96,7 @@ export type DocExplorerStoreType = {
};
};
-const INITIAL_NAV_STACK: DocExplorerNavStack = [{ name: 'Docs' }];
+const INITIAL_NAV_STACK: DocExplorerNavStack = [{ name: 'Root' }];
export const docExplorerStore = createStore(
(set, get) => ({
diff --git a/packages/graphiql-plugin-doc-explorer/src/stories/doc-explorer.stories.tsx b/packages/graphiql-plugin-doc-explorer/src/stories/doc-explorer.stories.tsx
index d699a79a48b..904ab8b8875 100644
--- a/packages/graphiql-plugin-doc-explorer/src/stories/doc-explorer.stories.tsx
+++ b/packages/graphiql-plugin-doc-explorer/src/stories/doc-explorer.stories.tsx
@@ -9,12 +9,16 @@ import {
GraphQLObjectType,
GraphQLSchema,
GraphQLString,
+ GraphQLUnionType,
} from 'graphql';
import { Tooltip } from '@graphiql/react';
import { DocExplorerStore } from '../context';
import { TypeDocumentation } from '../components/type-documentation';
import { FieldDocumentation } from '../components/field-documentation';
import { SchemaDocumentation } from '../components/schema-documentation';
+import { TypeCard } from '../components/type-card';
+import { FieldsList } from '../components/fields-list';
+import { Breadcrumb } from '../components/breadcrumb';
import { TypeLink } from '../components/type-link';
import { FieldLink } from '../components/field-link';
import { Argument } from '../components/argument';
@@ -87,6 +91,12 @@ const HumanType = new GraphQLObjectType({
}),
});
+const SearchResultUnion = new GraphQLUnionType({
+ name: 'SearchResult',
+ description: 'A result from a global search',
+ types: [HumanType],
+});
+
const QueryType = new GraphQLObjectType({
name: 'Query',
description: 'The root query type for the Star Wars API',
@@ -116,6 +126,10 @@ const QueryType = new GraphQLObjectType({
type: GraphQLBoolean,
deprecationReason: 'Use `hero` with status field instead',
},
+ search: {
+ type: SearchResultUnion,
+ description: 'Search across all types',
+ },
},
});
@@ -137,7 +151,13 @@ const StarWarsSchema = new GraphQLSchema({
description: 'The Star Wars GraphQL API',
query: QueryType,
mutation: MutationType,
- types: [HumanType, CharacterInterface, EpisodeEnum, ReviewInputType],
+ types: [
+ HumanType,
+ CharacterInterface,
+ EpisodeEnum,
+ ReviewInputType,
+ SearchResultUnion,
+ ],
});
function withDocExplorerStore(Story: React.FC) {
@@ -167,24 +187,51 @@ export const SchemaOverview: Story = {
},
};
-export const TypeDetail: Story = {
- name: 'Type detail (Human)',
- render: function TypeDetailStory() {
- return ;
+export const TypeDetailObject: Story = {
+ name: 'Type detail — Object',
+ render: function TypeDetailObjectStory() {
+ return (
+ <>
+
+
+ >
+ );
},
};
-export const EnumTypeDetail: Story = {
- name: 'Type detail (Enum)',
- render: function EnumTypeDetailStory() {
- return ;
+export const TypeDetailInterface: Story = {
+ name: 'Type detail — Interface',
+ render: function TypeDetailInterfaceStory() {
+ return (
+ <>
+
+
+ >
+ );
},
};
-export const InputTypeDetail: Story = {
- name: 'Type detail (Input)',
- render: function InputTypeDetailStory() {
- return ;
+export const TypeDetailInput: Story = {
+ name: 'Type detail — Input',
+ render: function TypeDetailInputStory() {
+ return (
+ <>
+
+
+ >
+ );
+ },
+};
+
+export const TypeDetailEnum: Story = {
+ name: 'Type detail — Enum',
+ render: function TypeDetailEnumStory() {
+ return (
+ <>
+
+
+ >
+ );
},
};
@@ -196,6 +243,18 @@ export const FieldDetail: Story = {
},
};
+export const BreadcrumbNav: Story = {
+ name: 'Breadcrumb navigation',
+ render: function BreadcrumbNavStory() {
+ const navStack: Parameters[0]['navStack'] = [
+ { name: 'Root' },
+ { name: 'Query', def: QueryType },
+ { name: 'Human', def: HumanType },
+ ];
+ return {}} />;
+ },
+};
+
export const TokenColors: Story = {
name: 'Token colors',
render: function TokenColorsStory() {
diff --git a/packages/graphiql/cypress/e2e/docs.cy.ts b/packages/graphiql/cypress/e2e/docs.cy.ts
index 031113d68a5..1f0b369c81f 100644
--- a/packages/graphiql/cypress/e2e/docs.cy.ts
+++ b/packages/graphiql/cypress/e2e/docs.cy.ts
@@ -31,12 +31,15 @@ describe('GraphiQL DocExplorer - search', () => {
it('Navigates to a docs entry on selecting a search result', () => {
cy.dataCy('doc-explorer-option').eq(4).children().click();
- cy.get('.graphiql-doc-explorer-title').should('have.text', 'TestInput');
+ cy.get('.graphiql-doc-explorer-breadcrumb-current').should(
+ 'have.text',
+ 'TestInput',
+ );
});
it('Allows searching fields within a type', () => {
cy.dataCy('doc-explorer-option').eq(4).children().click();
- cy.dataCy('doc-explorer-input').type('list');
+ cy.dataCy('doc-explorer-input').clear().type('list');
cy.dataCy('doc-explorer-option').should('have.length', 14);
cy.get('.graphiql-doc-explorer-search-divider').should(
'have.text',
@@ -54,16 +57,21 @@ describe('GraphiQL DocExplorer - search', () => {
it('Navigates back', () => {
cy.dataCy('doc-explorer-option').eq(4).children().click();
- cy.get('.graphiql-doc-explorer-back').click();
- cy.get('.graphiql-doc-explorer-title').should('have.text', 'Docs');
+ // Click the root breadcrumb segment (first link, at depth 0 = "Root")
+ cy.get('.graphiql-doc-explorer-breadcrumb-root').click();
+ // After navigating back, breadcrumb disappears (at root level, no breadcrumb shown)
+ cy.get('.graphiql-doc-explorer-breadcrumb').should('not.exist');
});
it('Type fields link to their own docs entry', () => {
cy.dataCy('doc-explorer-option').last().click();
- cy.get('.graphiql-doc-explorer-title').should('have.text', 'isTest');
- cy.get('.graphiql-markdown-description').should(
+ cy.get('.graphiql-doc-explorer-breadcrumb-current').should(
'have.text',
- 'Is this a test schema? Sure it is.\n',
+ 'isTest',
+ );
+ cy.get('.graphiql-doc-explorer-field-card-description').should(
+ 'have.text',
+ 'Is this a test schema? Sure it is.',
);
});
});
@@ -79,20 +87,21 @@ describe('GraphQL DocExplorer - deprecated fields', () => {
// Show deprecated fields
cy.contains('Show Deprecated Fields').click();
- // Assert that title is shown
- cy.get('.graphiql-doc-explorer-section-title').contains(
- 'Deprecated Fields',
- );
+ // Click into the deprecated field to view its documentation
+ cy.contains(
+ 'button.graphiql-doc-explorer-field-row--deprecated',
+ 'deprecatedField',
+ ).click();
- // Assert that the deprecated field is shown correctly
- cy.get('.graphiql-doc-explorer-field-name')
- .contains('deprecatedField')
- .closest('.graphiql-doc-explorer-item')
- .should('contain.text', 'This field is an example of a deprecated field')
- .and(
- 'contain.html',
- '
No longer in use, try test instead.
',
- );
+ // Assert description and deprecation reason are shown
+ cy.get('.graphiql-doc-explorer-field-card-description').should(
+ 'contain.text',
+ 'This field is an example of a deprecated field',
+ );
+ cy.get('.graphiql-markdown-deprecation').should(
+ 'contain.html',
+ '
No longer in use, try test instead.
',
+ );
});
});
@@ -107,13 +116,13 @@ describeOrSkip('GraphQL DocExplorer - deprecated arguments', () => {
// Open doc explorer
cy.get('.graphiql-activity-rail-item').eq(0).click();
- // Select query type
- cy.get('.graphiql-doc-explorer-type-name').first().click();
+ // Use search to navigate directly to hasArgs
+ cy.dataCy('doc-explorer-input').type('hasArgs');
+ cy.dataCy('doc-explorer-option').first().children().first().click();
- cy.get('.graphiql-doc-explorer-field-name').contains('hasArgs').click();
cy.contains('Show Deprecated Arguments').click();
- cy.get('.graphiql-doc-explorer-section-title').contains(
- 'Deprecated Arguments',
+ cy.get('.graphiql-doc-explorer-arguments-list-header').contains(
+ 'DEPRECATED ARGUMENTS',
);
cy.get('.graphiql-markdown-deprecation').should(
'have.text',