1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 | import {
|
9 | ExplorerFieldDef,
|
10 | useExplorerContext,
|
11 | useSchemaContext,
|
12 | } from '@graphiql/react';
|
13 | import {
|
14 | GraphQLEnumValue,
|
15 | GraphQLInterfaceType,
|
16 | GraphQLNamedType,
|
17 | GraphQLObjectType,
|
18 | isEnumType,
|
19 | isInterfaceType,
|
20 | isNamedType,
|
21 | isObjectType,
|
22 | isUnionType,
|
23 | } from 'graphql';
|
24 | import React, { ReactNode, useState } from 'react';
|
25 |
|
26 | import Argument from './Argument';
|
27 | import DefaultValue from './DefaultValue';
|
28 | import FieldLink from './FieldLink';
|
29 | import MarkdownContent from './MarkdownContent';
|
30 | import TypeLink from './TypeLink';
|
31 |
|
32 | export 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 |
|
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 |
|
173 | type FieldProps = {
|
174 | type: GraphQLNamedType;
|
175 | field: ExplorerFieldDef;
|
176 | };
|
177 |
|
178 | function 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 |
|
214 | type EnumValueProps = {
|
215 | value: GraphQLEnumValue;
|
216 | };
|
217 |
|
218 | function 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 | }
|