UNPKG

6.25 kBTypeScriptView Raw
1/**
2 * Copyright (c) 2021 GraphQL Contributors.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8import {
9 ExplorerFieldDef,
10 useExplorerContext,
11 useSchemaContext,
12} from '@graphiql/react';
13import {
14 GraphQLEnumValue,
15 GraphQLInterfaceType,
16 GraphQLNamedType,
17 GraphQLObjectType,
18 isEnumType,
19 isInterfaceType,
20 isNamedType,
21 isObjectType,
22 isUnionType,
23} from 'graphql';
24import React, { ReactNode, useState } from 'react';
25
26import Argument from './Argument';
27import DefaultValue from './DefaultValue';
28import FieldLink from './FieldLink';
29import MarkdownContent from './MarkdownContent';
30import TypeLink from './TypeLink';
31
32export default function TypeDoc() {
33 const { schema } = useSchemaContext({ nonNull: true });
34 const { explorerNavStack } = useExplorerContext({ nonNull: true });
35 const [showDeprecated, setShowDeprecated] = useState(false);
36
37 const navItem = explorerNavStack[explorerNavStack.length - 1];
38 const type = navItem.def;
39
40 if (!schema || !isNamedType(type)) {
41 return null;
42 }
43
44 let typesTitle: string | null = null;
45 let types: readonly (GraphQLObjectType | GraphQLInterfaceType)[] = [];
46 if (isUnionType(type)) {
47 typesTitle = 'possible types';
48 types = schema.getPossibleTypes(type);
49 } else if (isInterfaceType(type)) {
50 typesTitle = 'implementations';
51 types = schema.getPossibleTypes(type);
52 } else if (isObjectType(type)) {
53 typesTitle = 'implements';
54 types = type.getInterfaces();
55 }
56
57 let typesDef;
58 if (types && types.length > 0) {
59 typesDef = (
60 <div id="doc-types" className="doc-category">
61 <div className="doc-category-title">{typesTitle}</div>
62 {types.map(subtype => (
63 <div key={subtype.name} className="doc-category-item">
64 <TypeLink type={subtype} />
65 </div>
66 ))}
67 </div>
68 );
69 }
70
71 // InputObject and Object
72 let fieldsDef;
73 let deprecatedFieldsDef;
74 if (type && 'getFields' in type) {
75 const fieldMap = type.getFields();
76 const fields = Object.keys(fieldMap).map(name => fieldMap[name]);
77 fieldsDef = (
78 <div id="doc-fields" className="doc-category">
79 <div className="doc-category-title">fields</div>
80 {fields
81 .filter(field => !field.deprecationReason)
82 .map(field => (
83 <Field key={field.name} type={type} field={field} />
84 ))}
85 </div>
86 );
87
88 const deprecatedFields = fields.filter(field =>
89 Boolean(field.deprecationReason),
90 );
91 if (deprecatedFields.length > 0) {
92 deprecatedFieldsDef = (
93 <div id="doc-deprecated-fields" className="doc-category">
94 <div className="doc-category-title">deprecated fields</div>
95 {!showDeprecated ? (
96 <button
97 className="show-btn"
98 onClick={() => {
99 setShowDeprecated(true);
100 }}
101 >
102 Show deprecated fields...
103 </button>
104 ) : (
105 deprecatedFields.map(field => (
106 <Field key={field.name} type={type} field={field} />
107 ))
108 )}
109 </div>
110 );
111 }
112 }
113
114 let valuesDef: ReactNode;
115 let deprecatedValuesDef: ReactNode;
116 if (isEnumType(type)) {
117 const values = type.getValues();
118 valuesDef = (
119 <div className="doc-category">
120 <div className="doc-category-title">values</div>
121 {values
122 .filter(value => Boolean(!value.deprecationReason))
123 .map(value => (
124 <EnumValue key={value.name} value={value} />
125 ))}
126 </div>
127 );
128
129 const deprecatedValues = values.filter(value =>
130 Boolean(value.deprecationReason),
131 );
132 if (deprecatedValues.length > 0) {
133 deprecatedValuesDef = (
134 <div className="doc-category">
135 <div className="doc-category-title">deprecated values</div>
136 {!showDeprecated ? (
137 <button
138 className="show-btn"
139 onClick={() => {
140 setShowDeprecated(true);
141 }}
142 >
143 Show deprecated values...
144 </button>
145 ) : (
146 deprecatedValues.map(value => (
147 <EnumValue key={value.name} value={value} />
148 ))
149 )}
150 </div>
151 );
152 }
153 }
154
155 return (
156 <div>
157 <MarkdownContent
158 className="doc-type-description"
159 markdown={
160 ('description' in type && type.description) || 'No Description'
161 }
162 />
163 {isObjectType(type) && typesDef}
164 {fieldsDef}
165 {deprecatedFieldsDef}
166 {valuesDef}
167 {deprecatedValuesDef}
168 {!isObjectType(type) && typesDef}
169 </div>
170 );
171}
172
173type FieldProps = {
174 type: GraphQLNamedType;
175 field: ExplorerFieldDef;
176};
177
178function Field({ field }: FieldProps) {
179 return (
180 <div className="doc-category-item">
181 <FieldLink field={field} />
182 {'args' in field &&
183 field.args &&
184 field.args.length > 0 && [
185 '(',
186 <span key="args">
187 {field.args
188 .filter(arg => !arg.deprecationReason)
189 .map(arg => (
190 <Argument key={arg.name} arg={arg} />
191 ))}
192 </span>,
193 ')',
194 ]}
195 {': '}
196 <TypeLink type={field.type} />
197 <DefaultValue field={field} />
198 {field.description && (
199 <MarkdownContent
200 className="field-short-description"
201 markdown={field.description}
202 />
203 )}
204 {'deprecationReason' in field && field.deprecationReason && (
205 <MarkdownContent
206 className="doc-deprecation"
207 markdown={field.deprecationReason}
208 />
209 )}
210 </div>
211 );
212}
213
214type EnumValueProps = {
215 value: GraphQLEnumValue;
216};
217
218function EnumValue({ value }: EnumValueProps) {
219 return (
220 <div className="doc-category-item">
221 <div className="enum-value">{value.name}</div>
222 <MarkdownContent
223 className="doc-value-description"
224 markdown={value.description}
225 />
226 {value.deprecationReason && (
227 <MarkdownContent
228 className="doc-deprecation"
229 markdown={value.deprecationReason}
230 />
231 )}
232 </div>
233 );
234}