Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 56 additions & 6 deletions src/Explorer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2325,7 +2325,35 @@ function Attribution() {
</div>
);
}
type TypeSearchBarProps = {
onSearch: (string) => void,
value: string,
style?: Object,
};

class TypeSearchBar extends React.PureComponent<TypeSearchBarProps> {
render() {
return (
<div style={{ marginBottom: 8, padding: '4px 0' }}>
<input
type="text"
placeholder="Search types..."
value={this.props.value}
onChange={e => this.props.onSearch(e.target.value)}
style={{
width: '100%',
fontSize: 12,
padding: '4px 8px',
boxSizing: 'border-box',
border: '1px solid #ccc',
borderRadius: 3,
...this.props.style,
}}
/>
</div>
);
}
}
class Explorer extends React.PureComponent<Props, State> {
static defaultProps = {
getDefaultFieldNames: defaultGetDefaultFieldNames,
Expand All @@ -2336,6 +2364,7 @@ class Explorer extends React.PureComponent<Props, State> {
newOperationType: 'query',
operation: null,
operationToScrollTo: null,
typeSearch: ''
};

_ref: ?any;
Expand Down Expand Up @@ -2366,6 +2395,19 @@ class Explorer extends React.PureComponent<Props, State> {
el && el.scrollIntoView();
}
};
_setTypeSearch = (value: string) => {
this.setState({ typeSearch: value });
};

// --- Utility to filter type names by search ---
_filterFields = (fields: ?GraphQLFieldMap<any, any>, search: string) => {
if (!fields) return {};
if (!search.trim()) return fields;
const lower = search.trim().toLowerCase();
return Object.keys(fields)
.filter(fieldName => fieldName.toLowerCase().includes(lower))
.reduce((acc, name) => { acc[name] = fields[name]; return acc; }, {});
};

render() {
const {schema, query, makeDefaultArg} = this.props;
Expand Down Expand Up @@ -2698,7 +2740,10 @@ class Explorer extends React.PureComponent<Props, State> {
const availableFragments = {...documentFragments, ...externalFragments};

const attribution = this.props.showAttribution ? <Attribution /> : null;

const { typeSearch } = this.state;
const filteredQueryFields = this._filterFields(queryFields, typeSearch);
const filteredMutationFields = this._filterFields(mutationFields, typeSearch);
const filteredSubscriptionFields = this._filterFields(subscriptionFields, typeSearch);
return (
<div
ref={ref => {
Expand All @@ -2717,6 +2762,11 @@ class Explorer extends React.PureComponent<Props, State> {
height: '100%',
}}
className="graphiql-explorer-root">
<TypeSearchBar
value={typeSearch}
onSearch={this._setTypeSearch}
style={{ marginBottom: 12 }}
/>
<div
style={{
flexGrow: '1',
Expand Down Expand Up @@ -2760,15 +2810,15 @@ class Explorer extends React.PureComponent<Props, State> {
? fragmentType.getFields()
: null;

const fields =
let fields =
operationType === 'query'
? queryFields
? filteredQueryFields
: operationType === 'mutation'
? mutationFields
? filteredMutationFields
: operationType === 'subscription'
? subscriptionFields
? filteredSubscriptionFields
: operation.kind === 'FragmentDefinition'
? fragmentFields
? this._filterFields(fragmentFields, typeSearch)
: null;

const fragmentTypeName =
Expand Down