diff --git a/packages/graphql-playground-react/src/components/Playground/DocExplorer/SearchBox.tsx b/packages/graphql-playground-react/src/components/Playground/DocExplorer/SearchBox.tsx index e895a2dfb..81de748a7 100644 --- a/packages/graphql-playground-react/src/components/Playground/DocExplorer/SearchBox.tsx +++ b/packages/graphql-playground-react/src/components/Playground/DocExplorer/SearchBox.tsx @@ -44,6 +44,9 @@ export default class SearchBox extends React.Component { type="text" value={this.state.value} placeholder={this.props.placeholder || 'Search the docs ...'} + title={ + 'Wrap in quotes "" to match exactly. Prefix with e.g. Mutation. to only match mutations' + } /> ) diff --git a/packages/graphql-playground-react/src/components/Playground/DocExplorer/SearchResults.tsx b/packages/graphql-playground-react/src/components/Playground/DocExplorer/SearchResults.tsx index 7b42fd101..b28139c83 100644 --- a/packages/graphql-playground-react/src/components/Playground/DocExplorer/SearchResults.tsx +++ b/packages/graphql-playground-react/src/components/Playground/DocExplorer/SearchResults.tsx @@ -20,10 +20,30 @@ export default class SearchResults extends React.Component { render() { const { level } = this.props - const searchValue = this.props.searchValue + let searchValue = this.props.searchValue const withinType = this.props.withinType const schema = this.props.schema + let matchFn = isMatch + let exactMatch = false + if (searchValue.match(/^".+"$/)) { + exactMatch = true + matchFn = isExactMatch + searchValue = searchValue.slice(1, searchValue.length - 1) + } + + let subtypeSearch: null | string = null + const subtypeMatch = searchValue.match(/\./g) + if (!withinType && subtypeMatch && subtypeMatch.length === 1) { + const parts = searchValue.split('.') + const typePart = parts[0] + const newSearchValue = parts[1] + if (typePart.length > 0) { + subtypeSearch = typePart + searchValue = newSearchValue + } + } + const matchedWithin: any[] = [] const matchedTypes: any[] = [] const matchedFields: any[] = [] @@ -47,7 +67,11 @@ export default class SearchResults extends React.Component { } const type = typeMap[typeName] - if (withinType !== type && isMatch(typeName, searchValue)) { + if ( + withinType !== type && + matchFn(typeName, searchValue) && + !subtypeSearch + ) { matchedTypes.push(
@@ -55,17 +79,21 @@ export default class SearchResults extends React.Component { ) } - if (type.getFields) { + /* don't show match for e.g. Query.Asset when searching for exact match "Asset", + only show Asset, however, for "Query.Asset" we want to use the maching logic + within this if clause */ + if (type.getFields && (!exactMatch || (exactMatch && subtypeMatch))) { const fields = type.getFields() Object.keys(fields).forEach(fieldName => { const field = fields[fieldName] field.parent = type let matchingArgs - if (!isMatch(fieldName, searchValue)) { + /* when doing a Query.Asset search, don't match args and stuff within the query */ + if (!matchFn(fieldName, searchValue) && !subtypeSearch) { if (field.args && field.args.length) { matchingArgs = field.args.filter(arg => - isMatch(arg.name, searchValue), + matchFn(arg.name, searchValue), ) if (matchingArgs.length === 0) { return @@ -88,8 +116,17 @@ export default class SearchResults extends React.Component {
) + /* when doing a search within a type the subtypeSearch should be false */ if (withinType === type) { matchedWithin.push(match) + } else if (subtypeSearch) { + /* in e.g. Query.Asset search, both Query and Asset must match */ + if ( + matchFn(typeName, subtypeSearch) && + matchFn(fieldName, searchValue) + ) { + matchedFields.push(match) + } } else { matchedFields.push(match) } @@ -135,6 +172,9 @@ function isMatch(sourceText, searchValue) { return sourceText.toLowerCase().indexOf(searchValue.toLowerCase()) !== -1 } } +function isExactMatch(sourceText, searchValue) { + return sourceText === searchValue +} const NoResult = styled.span` display: block;