From 440c06ce197a270cc003ae02a40f015074e3746c Mon Sep 17 00:00:00 2001 From: Thomas Heyenbrock Date: Mon, 25 Jul 2022 13:59:45 +0200 Subject: [PATCH] [redesign] move docs components to `@graphiql/react` (#2582) * move explorer context into folder * move `TypeLink` component to `@graphiql/react` * move `FieldLink` component to `@graphiql/react` * move `DefaultValue` component to `@graphiql/react` * move `Directive` component to `@graphiql/react` * extract `MarkdownContent` component in `@gaphiql/react` * move `Argument` component to `@graphiql/react` * combine and extend changesets --- .changeset/fifty-elephants-divide.md | 5 -- .changeset/five-pillows-fail.md | 7 +- .changeset/fluffy-pianos-mix.md | 5 -- .changeset/long-gorillas-act.md | 5 -- .changeset/mean-chefs-happen.md | 5 -- .changeset/nice-garlics-help.md | 5 -- .changeset/perfect-flowers-hunt.md | 5 -- .changeset/serious-actors-accept.md | 5 -- .changeset/smooth-countries-shout.md | 5 -- .../components/__tests__/test-utils.ts | 22 ++++++ .../components/__tests__/type-link.spec.tsx} | 20 ++--- .../src/explorer/components/argument.css | 22 ++++++ .../src/explorer/components/argument.tsx | 45 +++++++++++ .../src/explorer/components/default-value.css | 3 + .../src/explorer/components/default-value.tsx | 34 +++++++++ .../src/explorer/components/directive.css | 3 + .../src/explorer/components/directive.tsx | 15 ++++ .../src/explorer/components/field-link.css | 12 +++ .../src/explorer/components/field-link.tsx | 23 ++++++ .../src/explorer/components/type-link.css | 12 +++ .../src/explorer/components/type-link.tsx} | 17 ++--- .../{explorer.tsx => explorer/context.tsx} | 8 +- packages/graphiql-react/src/explorer/index.ts | 17 +++++ packages/graphiql-react/src/index.ts | 6 +- packages/graphiql-react/src/markdown.ts | 5 +- packages/graphiql-react/src/ui/index.ts | 1 + .../src/{style => ui}/markdown.css | 8 ++ packages/graphiql-react/src/ui/markdown.tsx | 20 +++++ .../graphiql/__mocks__/@graphiql/react.tsx | 6 ++ .../graphiql/cypress/integration/docs.spec.ts | 22 ++++-- .../src/components/DocExplorer/Argument.tsx | 27 ------- .../components/DocExplorer/DefaultValue.tsx | 37 --------- .../src/components/DocExplorer/Directive.tsx | 21 ----- .../src/components/DocExplorer/FieldDoc.tsx | 69 +++++------------ .../src/components/DocExplorer/FieldLink.tsx | 30 -------- .../DocExplorer/MarkdownContent.tsx | 37 --------- .../src/components/DocExplorer/SchemaDoc.tsx | 15 ++-- .../components/DocExplorer/SearchResults.tsx | 12 +-- .../src/components/DocExplorer/TypeDoc.tsx | 62 +++++++-------- .../DocExplorer/__tests__/FieldDoc.spec.tsx | 76 ++++++++++++------- .../DocExplorer/__tests__/TypeDoc.spec.tsx | 22 +++--- .../components/__tests__/DocExplorer.spec.tsx | 6 +- 42 files changed, 412 insertions(+), 370 deletions(-) delete mode 100644 .changeset/fifty-elephants-divide.md delete mode 100644 .changeset/fluffy-pianos-mix.md delete mode 100644 .changeset/long-gorillas-act.md delete mode 100644 .changeset/mean-chefs-happen.md delete mode 100644 .changeset/nice-garlics-help.md delete mode 100644 .changeset/perfect-flowers-hunt.md delete mode 100644 .changeset/serious-actors-accept.md delete mode 100644 .changeset/smooth-countries-shout.md create mode 100644 packages/graphiql-react/src/explorer/components/__tests__/test-utils.ts rename packages/{graphiql/src/components/DocExplorer/__tests__/TypeLink.spec.tsx => graphiql-react/src/explorer/components/__tests__/type-link.spec.tsx} (81%) create mode 100644 packages/graphiql-react/src/explorer/components/argument.css create mode 100644 packages/graphiql-react/src/explorer/components/argument.tsx create mode 100644 packages/graphiql-react/src/explorer/components/default-value.css create mode 100644 packages/graphiql-react/src/explorer/components/default-value.tsx create mode 100644 packages/graphiql-react/src/explorer/components/directive.css create mode 100644 packages/graphiql-react/src/explorer/components/directive.tsx create mode 100644 packages/graphiql-react/src/explorer/components/field-link.css create mode 100644 packages/graphiql-react/src/explorer/components/field-link.tsx create mode 100644 packages/graphiql-react/src/explorer/components/type-link.css rename packages/{graphiql/src/components/DocExplorer/TypeLink.tsx => graphiql-react/src/explorer/components/type-link.tsx} (65%) rename packages/graphiql-react/src/{explorer.tsx => explorer/context.tsx} (94%) create mode 100644 packages/graphiql-react/src/explorer/index.ts rename packages/graphiql-react/src/{style => ui}/markdown.css (93%) create mode 100644 packages/graphiql-react/src/ui/markdown.tsx delete mode 100644 packages/graphiql/src/components/DocExplorer/Argument.tsx delete mode 100644 packages/graphiql/src/components/DocExplorer/DefaultValue.tsx delete mode 100644 packages/graphiql/src/components/DocExplorer/Directive.tsx delete mode 100644 packages/graphiql/src/components/DocExplorer/FieldLink.tsx delete mode 100644 packages/graphiql/src/components/DocExplorer/MarkdownContent.tsx diff --git a/.changeset/fifty-elephants-divide.md b/.changeset/fifty-elephants-divide.md deleted file mode 100644 index 1e65dc3f98d..00000000000 --- a/.changeset/fifty-elephants-divide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@graphiql/react': minor ---- - -Add a `Dropdown` ui component diff --git a/.changeset/five-pillows-fail.md b/.changeset/five-pillows-fail.md index 23eac38ee40..531e2191a1a 100644 --- a/.changeset/five-pillows-fail.md +++ b/.changeset/five-pillows-fail.md @@ -2,4 +2,9 @@ '@graphiql/react': minor --- -Add editor components and implement styles for the new GraphiQL design +Add new components: +- UI components (`Dropdown`, `Spinner`, `UnStyledButton` and lots of icon components) +- Editor components (`QueryEditor`, `VariableEditor`, `HeaderEditor` and `ResponseEditor`) +- Toolbar components (`ExecuteButton` and `ToolbarButton`) +- Docs components (`Argument`, `DefaultValue`, `Directive`, `FieldLink` and `TypeLink`) +- `History` component diff --git a/.changeset/fluffy-pianos-mix.md b/.changeset/fluffy-pianos-mix.md deleted file mode 100644 index fe497c4f21d..00000000000 --- a/.changeset/fluffy-pianos-mix.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@graphiql/react': minor ---- - -Add toolbar components (`ExecuteButton` and `ToolbarButton`) diff --git a/.changeset/long-gorillas-act.md b/.changeset/long-gorillas-act.md deleted file mode 100644 index 16feb2f659f..00000000000 --- a/.changeset/long-gorillas-act.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@graphiql/react': minor ---- - -Add `UnStyledButton` UI component diff --git a/.changeset/mean-chefs-happen.md b/.changeset/mean-chefs-happen.md deleted file mode 100644 index de248921071..00000000000 --- a/.changeset/mean-chefs-happen.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@graphiql/react': minor ---- - -Add a component for rendering the history plugin diff --git a/.changeset/nice-garlics-help.md b/.changeset/nice-garlics-help.md deleted file mode 100644 index 68667c4ac01..00000000000 --- a/.changeset/nice-garlics-help.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@graphiql/react': minor ---- - -Add some neat svg icons to `@graphiql/react` diff --git a/.changeset/perfect-flowers-hunt.md b/.changeset/perfect-flowers-hunt.md deleted file mode 100644 index ae1c2696bdb..00000000000 --- a/.changeset/perfect-flowers-hunt.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'graphiql': minor ---- - -Consume the editor components from `@graphiql/react` diff --git a/.changeset/serious-actors-accept.md b/.changeset/serious-actors-accept.md deleted file mode 100644 index cd32b5d4d9d..00000000000 --- a/.changeset/serious-actors-accept.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@graphiql/react': patch ---- - -Set background color for editor to white by default diff --git a/.changeset/smooth-countries-shout.md b/.changeset/smooth-countries-shout.md deleted file mode 100644 index 87e4ced66ac..00000000000 --- a/.changeset/smooth-countries-shout.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@graphiql/react': minor ---- - -Add `Spinner` component to `@graphiql/react` diff --git a/packages/graphiql-react/src/explorer/components/__tests__/test-utils.ts b/packages/graphiql-react/src/explorer/components/__tests__/test-utils.ts new file mode 100644 index 00000000000..25a0b185af5 --- /dev/null +++ b/packages/graphiql-react/src/explorer/components/__tests__/test-utils.ts @@ -0,0 +1,22 @@ +import { GraphQLNamedType, GraphQLType } from 'graphql'; + +import { ExplorerContextType, ExplorerNavStackItem } from '../../context'; + +export function mockExplorerContextValue( + navStackItem: ExplorerNavStackItem, +): ExplorerContextType { + return { + explorerNavStack: [navStackItem], + hide() {}, + isVisible: true, + pop() {}, + push() {}, + reset() {}, + show() {}, + showSearch() {}, + }; +} + +export function unwrapType(type: GraphQLType): GraphQLNamedType { + return 'ofType' in type ? unwrapType(type.ofType) : type; +} diff --git a/packages/graphiql/src/components/DocExplorer/__tests__/TypeLink.spec.tsx b/packages/graphiql-react/src/explorer/components/__tests__/type-link.spec.tsx similarity index 81% rename from packages/graphiql/src/components/DocExplorer/__tests__/TypeLink.spec.tsx rename to packages/graphiql-react/src/explorer/components/__tests__/type-link.spec.tsx index f152551c3de..5b7609e3bb5 100644 --- a/packages/graphiql/src/components/DocExplorer/__tests__/TypeLink.spec.tsx +++ b/packages/graphiql-react/src/explorer/components/__tests__/type-link.spec.tsx @@ -1,20 +1,13 @@ -/** - * Copyright (c) 2021 GraphQL Contributors. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -import { ExplorerContext } from '@graphiql/react'; import { // @ts-expect-error fireEvent, render, } from '@testing-library/react'; import { GraphQLNonNull, GraphQLList, GraphQLString } from 'graphql'; -import React, { ComponentProps } from 'react'; +import { ComponentProps } from 'react'; -import TypeLink from '../TypeLink'; +import { ExplorerContext } from '../../context'; +import { TypeLink } from '../type-link'; import { mockExplorerContextValue, unwrapType } from './test-utils'; const nonNullType = new GraphQLNonNull(GraphQLString); @@ -30,9 +23,11 @@ function TypeLinkWithContext(props: ComponentProps) { {/* Print the top of the current nav stack for test assertions */} - {({ explorerNavStack }) => ( + {context => ( - {JSON.stringify(explorerNavStack[explorerNavStack.length + 1])} + {JSON.stringify( + context!.explorerNavStack[context!.explorerNavStack.length + 1], + )} )} @@ -45,7 +40,6 @@ describe('TypeLink', () => { const { container } = render(); expect(container).toHaveTextContent('String'); expect(container.querySelectorAll('a')).toHaveLength(1); - expect(container.querySelector('a')).toHaveClass('type-name'); }); it('should render a non-null type', () => { const { container } = render(); diff --git a/packages/graphiql-react/src/explorer/components/argument.css b/packages/graphiql-react/src/explorer/components/argument.css new file mode 100644 index 00000000000..9602251ea43 --- /dev/null +++ b/packages/graphiql-react/src/explorer/components/argument.css @@ -0,0 +1,22 @@ +.graphiql-doc-explorer-argument { + & > * + * { + margin-top: var(--px-12); + } +} + +.graphiql-doc-explorer-argument-name { + color: var(--color-purple); +} + +.graphiql-doc-explorer-argument-deprecation { + background-color: var(--color-orche-background); + border: 1px solid var(--color-orche); + border-radius: var(--border-radius-4); + color: var(--color-orche); + padding: var(--px-8); +} + +.graphiql-doc-explorer-argument-deprecation-label { + font-size: var(--font-size-hint); + font-weight: var(--font-weight-medium); +} diff --git a/packages/graphiql-react/src/explorer/components/argument.tsx b/packages/graphiql-react/src/explorer/components/argument.tsx new file mode 100644 index 00000000000..10824e991cc --- /dev/null +++ b/packages/graphiql-react/src/explorer/components/argument.tsx @@ -0,0 +1,45 @@ +import { GraphQLArgument } from 'graphql'; + +import { DefaultValue } from './default-value'; +import { TypeLink } from './type-link'; + +import './argument.css'; +import { MarkdownContent } from '../../ui'; + +type ArgumentProps = { + arg: GraphQLArgument; + showDefaultValue?: boolean; + inline?: boolean; +}; + +export function Argument({ arg, showDefaultValue, inline }: ArgumentProps) { + const definition = ( + + {arg.name} + {': '} + + {showDefaultValue !== false && } + + ); + if (inline) { + return definition; + } + return ( +
+ {definition} + {arg.description ? ( + {arg.description} + ) : null} + {arg.deprecationReason ? ( +
+
+ Deprecated +
+ + {arg.deprecationReason} + +
+ ) : null} +
+ ); +} diff --git a/packages/graphiql-react/src/explorer/components/default-value.css b/packages/graphiql-react/src/explorer/components/default-value.css new file mode 100644 index 00000000000..dd45b3fb8a2 --- /dev/null +++ b/packages/graphiql-react/src/explorer/components/default-value.css @@ -0,0 +1,3 @@ +.graphiql-doc-explorer-default-value { + color: var(--color-green); +} diff --git a/packages/graphiql-react/src/explorer/components/default-value.tsx b/packages/graphiql-react/src/explorer/components/default-value.tsx new file mode 100644 index 00000000000..44ee3ff8cb7 --- /dev/null +++ b/packages/graphiql-react/src/explorer/components/default-value.tsx @@ -0,0 +1,34 @@ +import { astFromValue, print, ValueNode } from 'graphql'; + +import { ExplorerFieldDef } from '../context'; + +import './default-value.css'; + +const printDefault = (ast?: ValueNode | null): string => { + if (!ast) { + return ''; + } + return print(ast); +}; + +type DefaultValueProps = { + field: ExplorerFieldDef; +}; + +export function DefaultValue({ field }: DefaultValueProps) { + if (!('defaultValue' in field) || field.defaultValue === undefined) { + return null; + } + const ast = astFromValue(field.defaultValue, field.type); + if (!ast) { + return null; + } + return ( + <> + {' = '} + + {printDefault(ast)} + + + ); +} diff --git a/packages/graphiql-react/src/explorer/components/directive.css b/packages/graphiql-react/src/explorer/components/directive.css new file mode 100644 index 00000000000..90ccdf8109d --- /dev/null +++ b/packages/graphiql-react/src/explorer/components/directive.css @@ -0,0 +1,3 @@ +.graphiql-doc-explorer-directive { + color: var(--color-purple); +} diff --git a/packages/graphiql-react/src/explorer/components/directive.tsx b/packages/graphiql-react/src/explorer/components/directive.tsx new file mode 100644 index 00000000000..1ff48a3d3f4 --- /dev/null +++ b/packages/graphiql-react/src/explorer/components/directive.tsx @@ -0,0 +1,15 @@ +import { DirectiveNode } from 'graphql'; + +import './directive.css'; + +type DirectiveProps = { + directive: DirectiveNode; +}; + +export function Directive({ directive }: DirectiveProps) { + return ( + + @{directive.name.value} + + ); +} diff --git a/packages/graphiql-react/src/explorer/components/field-link.css b/packages/graphiql-react/src/explorer/components/field-link.css new file mode 100644 index 00000000000..363308335b6 --- /dev/null +++ b/packages/graphiql-react/src/explorer/components/field-link.css @@ -0,0 +1,12 @@ +.graphiql-doc-explorer-field-name { + color: var(--color-blue); + text-decoration: none; + + &:hover { + text-decoration: underline; + } + + &:focus { + outline: var(--color-blue) auto 1px; + } +} diff --git a/packages/graphiql-react/src/explorer/components/field-link.tsx b/packages/graphiql-react/src/explorer/components/field-link.tsx new file mode 100644 index 00000000000..4f30db3bcd0 --- /dev/null +++ b/packages/graphiql-react/src/explorer/components/field-link.tsx @@ -0,0 +1,23 @@ +import { ExplorerFieldDef, useExplorerContext } from '../context'; + +import './field-link.css'; + +type FieldLinkProps = { + field: ExplorerFieldDef; +}; + +export function FieldLink(props: FieldLinkProps) { + const { push } = useExplorerContext({ nonNull: true }); + + return ( + { + event.preventDefault(); + push({ name: props.field.name, def: props.field }); + }} + href="#"> + {props.field.name} + + ); +} diff --git a/packages/graphiql-react/src/explorer/components/type-link.css b/packages/graphiql-react/src/explorer/components/type-link.css new file mode 100644 index 00000000000..37ffcb35a98 --- /dev/null +++ b/packages/graphiql-react/src/explorer/components/type-link.css @@ -0,0 +1,12 @@ +.graphiql-doc-explorer-type-name { + color: var(--color-orche); + text-decoration: none; + + &:hover { + text-decoration: underline; + } + + &:focus { + outline: var(--color-orche) auto 1px; + } +} diff --git a/packages/graphiql/src/components/DocExplorer/TypeLink.tsx b/packages/graphiql-react/src/explorer/components/type-link.tsx similarity index 65% rename from packages/graphiql/src/components/DocExplorer/TypeLink.tsx rename to packages/graphiql-react/src/explorer/components/type-link.tsx index f87c768c366..912b3ed9502 100644 --- a/packages/graphiql/src/components/DocExplorer/TypeLink.tsx +++ b/packages/graphiql-react/src/explorer/components/type-link.tsx @@ -1,19 +1,14 @@ -/** - * Copyright (c) 2021 GraphQL Contributors. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -import { useExplorerContext } from '@graphiql/react'; import { GraphQLType, isListType, isNonNullType } from 'graphql'; -import React from 'react'; + +import { useExplorerContext } from '../context'; + +import './type-link.css'; type TypeLinkProps = { type: GraphQLType; }; -export default function TypeLink(props: TypeLinkProps) { +export function TypeLink(props: TypeLinkProps) { const { push } = useExplorerContext({ nonNull: true, caller: TypeLink }); if (!props.type) { @@ -37,7 +32,7 @@ export default function TypeLink(props: TypeLinkProps) { } return ( { event.preventDefault(); push({ name: type.name, def: type }); diff --git a/packages/graphiql-react/src/explorer.tsx b/packages/graphiql-react/src/explorer/context.tsx similarity index 94% rename from packages/graphiql-react/src/explorer.tsx rename to packages/graphiql-react/src/explorer/context.tsx index 41320002cda..7753ce9a449 100644 --- a/packages/graphiql-react/src/explorer.tsx +++ b/packages/graphiql-react/src/explorer/context.tsx @@ -12,10 +12,10 @@ import { useRef, useState, } from 'react'; -import { useSchemaContext } from './schema'; +import { useSchemaContext } from '../schema'; -import { useStorageContext } from './storage'; -import { createContextHook, createNullableContext } from './utility/context'; +import { useStorageContext } from '../storage'; +import { createContextHook, createNullableContext } from '../utility/context'; export type ExplorerFieldDef = | GraphQLField<{}, {}, {}> @@ -37,7 +37,7 @@ export type ExplorerNavStack = [ const initialNavStackItem: ExplorerNavStackItem = { name: 'Schema', - title: 'Documentation Explorer', + title: 'Docs', }; export type ExplorerContextType = { diff --git a/packages/graphiql-react/src/explorer/index.ts b/packages/graphiql-react/src/explorer/index.ts new file mode 100644 index 00000000000..4816ac6ddd8 --- /dev/null +++ b/packages/graphiql-react/src/explorer/index.ts @@ -0,0 +1,17 @@ +export { Argument } from './components/argument'; +export { DefaultValue } from './components/default-value'; +export { Directive } from './components/directive'; +export { FieldLink } from './components/field-link'; +export { TypeLink } from './components/type-link'; +export { + ExplorerContext, + ExplorerContextProvider, + useExplorerContext, +} from './context'; + +export type { + ExplorerContextType, + ExplorerFieldDef, + ExplorerNavStack, + ExplorerNavStackItem, +} from './context'; diff --git a/packages/graphiql-react/src/index.ts b/packages/graphiql-react/src/index.ts index 37f1dbdb4bd..38b6373ee4c 100644 --- a/packages/graphiql-react/src/index.ts +++ b/packages/graphiql-react/src/index.ts @@ -23,8 +23,13 @@ export { useExecutionContext, } from './execution'; export { + Argument, + DefaultValue, + Directive, ExplorerContext, ExplorerContextProvider, + FieldLink, + TypeLink, useExplorerContext, } from './explorer'; export { @@ -70,4 +75,3 @@ export type { SchemaContextType } from './schema'; export type { StorageContextType } from './storage'; import './style/root.css'; -import './style/markdown.css'; diff --git a/packages/graphiql-react/src/markdown.ts b/packages/graphiql-react/src/markdown.ts index 92efb783e51..9386e370973 100644 --- a/packages/graphiql-react/src/markdown.ts +++ b/packages/graphiql-react/src/markdown.ts @@ -1,3 +1,6 @@ import MarkdownIt from 'markdown-it'; -export const markdown = new MarkdownIt(); +export const markdown = new MarkdownIt({ + breaks: true, + linkify: true, +}); diff --git a/packages/graphiql-react/src/ui/index.ts b/packages/graphiql-react/src/ui/index.ts index 0399c089591..1051888c6f5 100644 --- a/packages/graphiql-react/src/ui/index.ts +++ b/packages/graphiql-react/src/ui/index.ts @@ -1,3 +1,4 @@ export * from './button'; export * from './dropdown'; +export * from './markdown'; export * from './spinner'; diff --git a/packages/graphiql-react/src/style/markdown.css b/packages/graphiql-react/src/ui/markdown.css similarity index 93% rename from packages/graphiql-react/src/style/markdown.css rename to packages/graphiql-react/src/ui/markdown.css index 5e68f639003..4e3acde1ce0 100644 --- a/packages/graphiql-react/src/style/markdown.css +++ b/packages/graphiql-react/src/ui/markdown.css @@ -6,6 +6,8 @@ * (field description any optionally deprecation reason). */ +.graphiql-markdown-description, +.graphiql-markdown-deprecation, .CodeMirror-hint-information-description, .CodeMirror-hint-information-deprecation-reason, .CodeMirror-info .info-description, @@ -66,6 +68,7 @@ } } +.graphiql-markdown-description, .CodeMirror-hint-information-description, .CodeMirror-info .info-description { & a { @@ -92,6 +95,7 @@ } } +.graphiql-markdown-deprecation, .CodeMirror-hint-information-deprecation-reason, .CodeMirror-info .info-deprecation { & a { @@ -113,6 +117,10 @@ } } +.graphiql-markdown-preview > :not(:first-child) { + display: none; +} + /** * We show deprecations in the following places: * - In the hint tooltip when typing in the query editor. diff --git a/packages/graphiql-react/src/ui/markdown.tsx b/packages/graphiql-react/src/ui/markdown.tsx new file mode 100644 index 00000000000..8bcdf399aec --- /dev/null +++ b/packages/graphiql-react/src/ui/markdown.tsx @@ -0,0 +1,20 @@ +import { markdown } from '../markdown'; + +import './markdown.css'; + +type MarkdownContentProps = { + children: string; + onlyShowFirstChild?: boolean; + type: 'description' | 'deprecation'; +}; + +export function MarkdownContent(props: MarkdownContentProps) { + return ( +
+ ); +} diff --git a/packages/graphiql/__mocks__/@graphiql/react.tsx b/packages/graphiql/__mocks__/@graphiql/react.tsx index 0f65bb6ac1a..8ba428bb092 100644 --- a/packages/graphiql/__mocks__/@graphiql/react.tsx +++ b/packages/graphiql/__mocks__/@graphiql/react.tsx @@ -12,9 +12,12 @@ import { import React, { useEffect, useRef, useState } from 'react'; export { + Argument, ChevronDownIcon, ChevronUpIcon, CopyIcon, + DefaultValue, + Directive, DocsIcon, Dropdown, EditorContext, @@ -24,6 +27,7 @@ export { ExecutionContextProvider, ExplorerContext, ExplorerContextProvider, + FieldLink, History, HistoryContext, HistoryContextProvider, @@ -31,6 +35,7 @@ export { ImagePreview, KeyboardShortcutIcon, onHasCompletion, + MarkdownContent, MergeIcon, PlayIcon, PrettifyIcon, @@ -42,6 +47,7 @@ export { StorageContext, StorageContextProvider, ToolbarButton, + TypeLink, UnStyledButton, useAutoCompleteLeafs, useCopyQuery, diff --git a/packages/graphiql/cypress/integration/docs.spec.ts b/packages/graphiql/cypress/integration/docs.spec.ts index 0a2a179c2f9..768948cce49 100644 --- a/packages/graphiql/cypress/integration/docs.spec.ts +++ b/packages/graphiql/cypress/integration/docs.spec.ts @@ -39,12 +39,15 @@ describe('GraphiQL DocExplorer - search', () => { it('Shows "other results" section', () => { cy.get('.doc-category-title').should('have.text', 'other results'); - cy.get('.doc-category .field-name').should('have.text', 'hasArgs'); + cy.get('.doc-category .graphiql-doc-explorer-field-name').should( + 'have.text', + 'hasArgs', + ); }); it('Navigates back to search results when existing', () => { cy.get('.doc-explorer-back').click(); - cy.get('.doc-explorer-title').should('have.text', 'Documentation Explorer'); + cy.get('.doc-explorer-title').should('have.text', 'Docs'); }); it('Retains the parent search value', () => { @@ -58,7 +61,7 @@ describe('GraphiQL DocExplorer - search', () => { .click(); cy.get('.doc-explorer-title').should('have.text', 'isTest'); - cy.get('.doc-type-description').should( + cy.get('.graphiql-markdown-description').should( 'have.text', 'Is this a test schema? Sure it is.\n', ); @@ -92,10 +95,10 @@ describe('GraphQL DocExplorer - deprecated fields', () => { const deprecated = cy.get('.doc-category').last(); deprecated - .get('.field-short-description') + .get('.graphiql-markdown-description') .should('contain.text', 'This field is an example of a deprecated field'); deprecated - .get('.doc-deprecation') + .get('.graphiql-markdown-deprecation') .should( 'contain.html', '

No longer in use, try test instead.

', @@ -112,11 +115,16 @@ if (!version.includes('15.5')) { describeOrSkip('GraphQL DocExplorer - deprecated arguments', () => { it('should show deprecated arguments category title', () => { - cy.get('#doc-fields .doc-category-item a.field-name').last().click(); + cy.get('#doc-fields .doc-category-item a.graphiql-doc-explorer-field-name') + .last() + .click(); cy.get('#doc-deprecated-args>.doc-category-title') .last() .should('have.text', 'deprecated arguments'); cy.get('.show-btn').click(); - cy.get('.doc-deprecation').should('have.text', 'deprecated argument\n'); + cy.get('.graphiql-markdown-deprecation').should( + 'have.text', + 'deprecated argument\n', + ); }); }); diff --git a/packages/graphiql/src/components/DocExplorer/Argument.tsx b/packages/graphiql/src/components/DocExplorer/Argument.tsx deleted file mode 100644 index 15a848086e2..00000000000 --- a/packages/graphiql/src/components/DocExplorer/Argument.tsx +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (c) 2021 GraphQL Contributors. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -import React from 'react'; -import { GraphQLArgument } from 'graphql'; -import TypeLink from './TypeLink'; -import DefaultValue from './DefaultValue'; - -type ArgumentProps = { - arg: GraphQLArgument; - showDefaultValue?: boolean; -}; - -export default function Argument({ arg, showDefaultValue }: ArgumentProps) { - return ( - - {arg.name} - {': '} - - {showDefaultValue !== false && } - - ); -} diff --git a/packages/graphiql/src/components/DocExplorer/DefaultValue.tsx b/packages/graphiql/src/components/DocExplorer/DefaultValue.tsx deleted file mode 100644 index 75d8c9866d2..00000000000 --- a/packages/graphiql/src/components/DocExplorer/DefaultValue.tsx +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright (c) 2021 GraphQL Contributors. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -import React from 'react'; -import { astFromValue, print, ValueNode } from 'graphql'; -import { ExplorerFieldDef } from '@graphiql/react'; - -const printDefault = (ast?: ValueNode | null): string => { - if (!ast) { - return ''; - } - return print(ast); -}; - -type DefaultValueProps = { - field: ExplorerFieldDef; -}; - -export default function DefaultValue({ field }: DefaultValueProps) { - // field.defaultValue could be null or false, so be careful here! - if ('defaultValue' in field && field.defaultValue !== undefined) { - return ( - - {' = '} - - {printDefault(astFromValue(field.defaultValue, field.type))} - - - ); - } - - return null; -} diff --git a/packages/graphiql/src/components/DocExplorer/Directive.tsx b/packages/graphiql/src/components/DocExplorer/Directive.tsx deleted file mode 100644 index 6c6c90493e1..00000000000 --- a/packages/graphiql/src/components/DocExplorer/Directive.tsx +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (c) 2021 GraphQL Contributors. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -import React from 'react'; -import { DirectiveNode } from 'graphql'; - -type DirectiveProps = { - directive: DirectiveNode; -}; - -export default function Directive({ directive }: DirectiveProps) { - return ( - - @{directive.name.value} - - ); -} diff --git a/packages/graphiql/src/components/DocExplorer/FieldDoc.tsx b/packages/graphiql/src/components/DocExplorer/FieldDoc.tsx index 53ec1824f5a..7b4bac6bfb6 100644 --- a/packages/graphiql/src/components/DocExplorer/FieldDoc.tsx +++ b/packages/graphiql/src/components/DocExplorer/FieldDoc.tsx @@ -5,14 +5,15 @@ * LICENSE file in the root directory of this source tree. */ +import { + Argument, + Directive, + MarkdownContent, + TypeLink, + useExplorerContext, +} from '@graphiql/react'; +import { DirectiveNode, isType } from 'graphql'; import React from 'react'; -import { GraphQLArgument, DirectiveNode, isType } from 'graphql'; -import { useExplorerContext } from '@graphiql/react'; - -import Argument from './Argument'; -import Directive from './Directive'; -import MarkdownContent from './MarkdownContent'; -import TypeLink from './TypeLink'; export default function FieldDoc() { const { explorerNavStack } = useExplorerContext({ nonNull: true }); @@ -32,22 +33,8 @@ export default function FieldDoc() {
arguments
{field.args .filter(arg => !arg.deprecationReason) - .map((arg: GraphQLArgument) => ( -
-
- -
- - {arg && 'deprecationReason' in arg && ( - - )} -
+ .map(arg => ( + ))}
); @@ -65,23 +52,7 @@ export default function FieldDoc() { Show deprecated arguments... ) : ( - deprecatedArgs.map((arg, i) => ( -
-
- -
- - {arg && 'deprecationReason' in arg && ( - - )} -
- )) + deprecatedArgs.map(arg => ) )} ); @@ -106,16 +77,14 @@ export default function FieldDoc() { return (
- - {field && 'deprecationReason' in field && ( - - )} + + {field.description || 'No Description'} + + {field && 'deprecationReason' in field && field.deprecationReason ? ( + + {field.deprecationReason} + + ) : null}
type
diff --git a/packages/graphiql/src/components/DocExplorer/FieldLink.tsx b/packages/graphiql/src/components/DocExplorer/FieldLink.tsx deleted file mode 100644 index a5d9d5caaa8..00000000000 --- a/packages/graphiql/src/components/DocExplorer/FieldLink.tsx +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (c) 2022 GraphQL Contributors. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -import React from 'react'; - -import { ExplorerFieldDef, useExplorerContext } from '@graphiql/react'; - -type FieldLinkProps = { - field: ExplorerFieldDef; -}; - -export default function FieldLink(props: FieldLinkProps) { - const { push } = useExplorerContext({ nonNull: true }); - - return ( -
{ - event.preventDefault(); - push({ name: props.field.name, def: props.field }); - }} - href="#"> - {props.field.name} - - ); -} diff --git a/packages/graphiql/src/components/DocExplorer/MarkdownContent.tsx b/packages/graphiql/src/components/DocExplorer/MarkdownContent.tsx deleted file mode 100644 index 0eb0a74f753..00000000000 --- a/packages/graphiql/src/components/DocExplorer/MarkdownContent.tsx +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright (c) 2021 GraphQL Contributors. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -import React from 'react'; -import MD from 'markdown-it'; -import { Maybe } from '../../types'; - -const md = new MD({ - // render urls as links, à la github-flavored markdown - breaks: true, - linkify: true, -}); - -type MarkdownContentProps = { - markdown?: Maybe; - className?: string; -}; - -export default function MarkdownContent({ - markdown, - className, -}: MarkdownContentProps) { - if (!markdown) { - return
; - } - - return ( -
- ); -} diff --git a/packages/graphiql/src/components/DocExplorer/SchemaDoc.tsx b/packages/graphiql/src/components/DocExplorer/SchemaDoc.tsx index 303a6260a35..c1ca10232dc 100644 --- a/packages/graphiql/src/components/DocExplorer/SchemaDoc.tsx +++ b/packages/graphiql/src/components/DocExplorer/SchemaDoc.tsx @@ -5,10 +5,8 @@ * LICENSE file in the root directory of this source tree. */ +import { MarkdownContent, TypeLink, useSchemaContext } from '@graphiql/react'; import React from 'react'; -import TypeLink from './TypeLink'; -import MarkdownContent from './MarkdownContent'; -import { useSchemaContext } from '@graphiql/react'; // Render the top level Schema export default function SchemaDoc() { @@ -24,13 +22,10 @@ export default function SchemaDoc() { return (
- + + {schema.description || + 'A GraphQL schema provides a root type for each kind of operation.'} +
root types
{queryType ? ( diff --git a/packages/graphiql/src/components/DocExplorer/SearchResults.tsx b/packages/graphiql/src/components/DocExplorer/SearchResults.tsx index 1e282e0dac4..b38e70fd466 100644 --- a/packages/graphiql/src/components/DocExplorer/SearchResults.tsx +++ b/packages/graphiql/src/components/DocExplorer/SearchResults.tsx @@ -5,13 +5,15 @@ * LICENSE file in the root directory of this source tree. */ +import { + Argument, + FieldLink, + TypeLink, + useExplorerContext, + useSchemaContext, +} from '@graphiql/react'; import React, { ReactNode } from 'react'; -import Argument from './Argument'; -import TypeLink from './TypeLink'; -import { useExplorerContext, useSchemaContext } from '@graphiql/react'; -import FieldLink from './FieldLink'; - export default function SearchResults() { const { explorerNavStack } = useExplorerContext({ nonNull: true }); const { schema } = useSchemaContext({ nonNull: true }); diff --git a/packages/graphiql/src/components/DocExplorer/TypeDoc.tsx b/packages/graphiql/src/components/DocExplorer/TypeDoc.tsx index 8be2fcf533a..e01002ba215 100644 --- a/packages/graphiql/src/components/DocExplorer/TypeDoc.tsx +++ b/packages/graphiql/src/components/DocExplorer/TypeDoc.tsx @@ -6,7 +6,12 @@ */ import { + Argument, + DefaultValue, ExplorerFieldDef, + FieldLink, + MarkdownContent, + TypeLink, useExplorerContext, useSchemaContext, } from '@graphiql/react'; @@ -23,12 +28,6 @@ import { } from 'graphql'; import React, { ReactNode, useState } from 'react'; -import Argument from './Argument'; -import DefaultValue from './DefaultValue'; -import FieldLink from './FieldLink'; -import MarkdownContent from './MarkdownContent'; -import TypeLink from './TypeLink'; - export default function TypeDoc() { const { schema } = useSchemaContext({ nonNull: true }); const { explorerNavStack } = useExplorerContext({ nonNull: true }); @@ -152,12 +151,9 @@ export default function TypeDoc() { return (
- + + {('description' in type && type.description) || 'No Description'} + {isObjectType(type) && typesDef} {fieldsDef} {deprecatedFieldsDef} @@ -193,18 +189,16 @@ function Field({ field }: FieldProps) { {': '} - {field.description && ( - - )} - {'deprecationReason' in field && field.deprecationReason && ( - - )} + {field.description ? ( + + {field.description} + + ) : null} + {'deprecationReason' in field && field.deprecationReason ? ( + + {field.deprecationReason} + + ) : null}
); } @@ -217,16 +211,16 @@ function EnumValue({ value }: EnumValueProps) { return (
{value.name}
- - {value.deprecationReason && ( - - )} + {value.description ? ( + + {value.description} + + ) : null} + {value.deprecationReason ? ( + + {value.deprecationReason} + + ) : null}
); } diff --git a/packages/graphiql/src/components/DocExplorer/__tests__/FieldDoc.spec.tsx b/packages/graphiql/src/components/DocExplorer/__tests__/FieldDoc.spec.tsx index 74de7581b54..1ee3fd537b3 100644 --- a/packages/graphiql/src/components/DocExplorer/__tests__/FieldDoc.spec.tsx +++ b/packages/graphiql/src/components/DocExplorer/__tests__/FieldDoc.spec.tsx @@ -82,49 +82,67 @@ describe('FieldDoc', () => { const { container } = render( , ); - expect(container.querySelector('.doc-type-description')).toHaveTextContent( - 'No Description', - ); - expect(container.querySelector('.type-name')).toHaveTextContent('String'); - expect(container.querySelector('.arg')).not.toBeInTheDocument(); + expect( + container.querySelector('.graphiql-markdown-description'), + ).toHaveTextContent('No Description'); + expect( + container.querySelector('.graphiql-doc-explorer-type-name'), + ).toHaveTextContent('String'); + expect( + container.querySelector('.graphiql-doc-explorer-argument'), + ).not.toBeInTheDocument(); }); it('should re-render on field change', () => { const { container, rerender } = render( , ); - expect(container.querySelector('.doc-type-description')).toHaveTextContent( - 'No Description', - ); - expect(container.querySelector('.type-name')).toHaveTextContent('String'); - expect(container.querySelector('.arg')).not.toBeInTheDocument(); + expect( + container.querySelector('.graphiql-markdown-description'), + ).toHaveTextContent('No Description'); + expect( + container.querySelector('.graphiql-doc-explorer-type-name'), + ).toHaveTextContent('String'); + expect( + container.querySelector('.graphiql-doc-explorer-argument'), + ).not.toBeInTheDocument(); rerender( , ); - expect(container.querySelector('.type-name')).toHaveTextContent('String'); - expect(container.querySelector('.doc-type-description')).toHaveTextContent( - 'Example String field with arguments', - ); + expect( + container.querySelector('.graphiql-doc-explorer-type-name'), + ).toHaveTextContent('String'); + expect( + container.querySelector('.graphiql-markdown-description'), + ).toHaveTextContent('Example String field with arguments'); }); it('should render a string field with arguments', () => { const { container } = render( , ); - expect(container.querySelector('.type-name')).toHaveTextContent('String'); - expect(container.querySelector('.doc-type-description')).toHaveTextContent( - 'Example String field with arguments', - ); - expect(container.querySelectorAll('.arg')).toHaveLength(1); - expect(container.querySelector('.arg')).toHaveTextContent( - 'stringArg: String', - ); + expect( + container.querySelector('.graphiql-doc-explorer-type-name'), + ).toHaveTextContent('String'); + expect( + container.querySelector('.graphiql-markdown-description'), + ).toHaveTextContent('Example String field with arguments'); + expect( + container.querySelectorAll('.graphiql-doc-explorer-argument'), + ).toHaveLength(1); + expect( + container.querySelector('.graphiql-doc-explorer-argument'), + ).toHaveTextContent('stringArg: String'); // by default, the deprecation docs should be hidden - expect(container.querySelectorAll('.doc-deprecation')).toHaveLength(0); + expect( + container.querySelectorAll('.graphiql-markdown-deprecation'), + ).toHaveLength(0); // make sure deprecation is present fireEvent.click(container.querySelector('.show-btn')); - const deprecationDocs = container.querySelectorAll('.doc-deprecation'); + const deprecationDocs = container.querySelectorAll( + '.graphiql-markdown-deprecation', + ); expect(deprecationDocs).toHaveLength(1); expect(deprecationDocs[0]).toHaveTextContent('no longer used'); }); @@ -135,9 +153,11 @@ describe('FieldDoc', () => { field={exampleObject.getFields().stringWithDirective} />, ); - expect(container.querySelector('.type-name')).toHaveTextContent('String'); - expect(container.querySelector('#development')).toHaveTextContent( - '@development', - ); + expect( + container.querySelector('.graphiql-doc-explorer-type-name'), + ).toHaveTextContent('String'); + expect( + container.querySelector('.graphiql-doc-explorer-directive'), + ).toHaveTextContent('@development'); }); }); diff --git a/packages/graphiql/src/components/DocExplorer/__tests__/TypeDoc.spec.tsx b/packages/graphiql/src/components/DocExplorer/__tests__/TypeDoc.spec.tsx index 0c410b3e3a9..1f58346f827 100644 --- a/packages/graphiql/src/components/DocExplorer/__tests__/TypeDoc.spec.tsx +++ b/packages/graphiql/src/components/DocExplorer/__tests__/TypeDoc.spec.tsx @@ -48,7 +48,9 @@ function TypeDocWithContext(props: { type: GraphQLNamedType }) { describe('TypeDoc', () => { it('renders a top-level query object type', () => { const { container } = render(); - const description = container.querySelectorAll('.doc-type-description'); + const description = container.querySelectorAll( + '.graphiql-markdown-description', + ); expect(description).toHaveLength(1); expect(description[0]).toHaveTextContent('Query description\nSecond line', { normalizeWhitespace: false, @@ -71,12 +73,12 @@ describe('TypeDoc', () => { cats = container.querySelectorAll('.doc-category-item'); expect(cats).toHaveLength(4); - expect(container.querySelectorAll('.field-name')[3]).toHaveTextContent( - 'deprecatedField', - ); - expect(container.querySelector('.doc-deprecation')).toHaveTextContent( - 'example deprecation reason', - ); + expect( + container.querySelectorAll('.graphiql-doc-explorer-field-name')[3], + ).toHaveTextContent('deprecatedField'); + expect( + container.querySelector('.graphiql-markdown-deprecation'), + ).toHaveTextContent('example deprecation reason'); }); it('renders a Union type', () => { @@ -114,8 +116,8 @@ describe('TypeDoc', () => { enums = container.querySelectorAll('.enum-value'); expect(enums).toHaveLength(3); expect(enums[2]).toHaveTextContent('value3'); - expect(container.querySelector('.doc-deprecation')).toHaveTextContent( - 'Only two are needed', - ); + expect( + container.querySelector('.graphiql-markdown-deprecation'), + ).toHaveTextContent('Only two are needed'); }); }); diff --git a/packages/graphiql/src/components/__tests__/DocExplorer.spec.tsx b/packages/graphiql/src/components/__tests__/DocExplorer.spec.tsx index d03a105bc27..c72cb11d279 100644 --- a/packages/graphiql/src/components/__tests__/DocExplorer.spec.tsx +++ b/packages/graphiql/src/components/__tests__/DocExplorer.spec.tsx @@ -67,8 +67,8 @@ describe('DocExplorer', () => { ); const error = container.querySelectorAll('.error-container'); expect(error).toHaveLength(0); - expect(container.querySelector('.doc-type-description')).toHaveTextContent( - 'GraphQL Schema for testing', - ); + expect( + container.querySelector('.graphiql-markdown-description'), + ).toHaveTextContent('GraphQL Schema for testing'); }); });