Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split DocExplorer into smaller files #243

Merged
merged 7 commits into from
Dec 10, 2016
Merged
Show file tree
Hide file tree
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
518 changes: 7 additions & 511 deletions src/components/DocExplorer.js

Large diffs are not rendered by default.

74 changes: 74 additions & 0 deletions src/components/DocExplorer/FieldDoc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* Copyright (c) 2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE-examples file in the root directory of this source tree.
*/

import React, { PropTypes } from 'react';

import MarkdownContent from './MarkdownContent';
import TypeLink from './TypeLink';

export default class FieldDoc extends React.Component {
static propTypes = {
field: PropTypes.object,
onClickType: PropTypes.func,
}

shouldComponentUpdate(nextProps) {
return this.props.field !== nextProps.field;
}

render() {
const field = this.props.field;

let argsDef;
if (field.args && field.args.length > 0) {
argsDef = (
<div className="doc-category">
<div className="doc-category-title">
{'arguments'}
</div>
{field.args.map(arg =>
<div key={arg.name} className="doc-category-item">
<div>
<span className="arg-name">{arg.name}</span>
{': '}
<TypeLink type={arg.type} onClick={this.props.onClickType} />
</div>
<MarkdownContent
className="doc-value-description"
markdown={arg.description}
/>
</div>
)}
</div>
);
}

return (
<div>
<MarkdownContent
className="doc-type-description"
markdown={field.description || 'No Description'}
/>
{
field.deprecationReason &&
<MarkdownContent
className="doc-alert-text"
markdown={field.deprecationReason}
/>
}
<div className="doc-category">
<div className="doc-category-title">
{'type'}
</div>
<TypeLink type={field.type} onClick={this.props.onClickType} />
</div>
{argsDef}
</div>
);
}
}
36 changes: 36 additions & 0 deletions src/components/DocExplorer/MarkdownContent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* Copyright (c) 2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE-examples file in the root directory of this source tree.
*/

import React, { PropTypes } from 'react';
import Marked from 'marked';

export default class MarkdownContent extends React.Component {
static propTypes = {
markdown: PropTypes.string,
className: PropTypes.string,
}

shouldComponentUpdate(nextProps) {
return this.props.markdown !== nextProps.markdown;
}

render() {
const markdown = this.props.markdown;
if (!markdown) {
return <div />;
}

const html = Marked(markdown, { sanitize: true });
return (
<div
className={this.props.className}
dangerouslySetInnerHTML={{ __html: html }}
/>
);
}
}
72 changes: 72 additions & 0 deletions src/components/DocExplorer/SchemaDoc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* Copyright (c) 2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE-examples file in the root directory of this source tree.
*/

import React, { PropTypes } from 'react';

import TypeLink from './TypeLink';
import MarkdownContent from './MarkdownContent';

// Render the top level Schema
export default class SchemaDoc extends React.Component {
static propTypes = {
schema: PropTypes.object,
onClickType: PropTypes.func,
}

shouldComponentUpdate(nextProps) {
return this.props.schema !== nextProps.schema;
}

render() {
const schema = this.props.schema;
const queryType = schema.getQueryType();
const mutationType = schema.getMutationType && schema.getMutationType();
const subscriptionType =
schema.getSubscriptionType && schema.getSubscriptionType();

return (
<div>
<MarkdownContent
className="doc-type-description"
markdown={
'A GraphQL schema provides a root type for each kind of operation.'
}
/>
<div className="doc-category">
<div className="doc-category-title">
{'root types'}
</div>
<div className="doc-category-item">
<span className="keyword">{'query'}</span>
{': '}
<TypeLink type={queryType} onClick={this.props.onClickType} />
</div>
{
mutationType &&
<div className="doc-category-item">
<span className="keyword">{'mutation'}</span>
{': '}
<TypeLink type={mutationType} onClick={this.props.onClickType} />
</div>
}
{
subscriptionType &&
<div className="doc-category-item">
<span className="keyword">{'subscription'}</span>
{': '}
<TypeLink
type={subscriptionType}
onClick={this.props.onClickType}
/>
</div>
}
</div>
</div>
);
}
}
56 changes: 56 additions & 0 deletions src/components/DocExplorer/SearchBox.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**
* Copyright (c) 2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE-examples file in the root directory of this source tree.
*/

import React, { PropTypes } from 'react';

import debounce from '../../utility/debounce';

export default class SearchBox extends React.Component {
static propTypes = {
isShown: PropTypes.bool,
onSearch: PropTypes.func,
}

constructor(props) {
super(props);

this.state = { value: '' };

this._debouncedOnSearch = debounce(200, () => {
this.props.onSearch(this.state.value);
});
}

shouldComponentUpdate(nextProps, nextState) {
return nextProps.isShown !== this.props.isShown ||
nextState.value !== this.state.value;
}

render() {
return (
<div>
{
this.props.isShown &&
<label className="search-box-outer">
<input className="search-box-input"
onChange={this.handleChange}
type="text"
value={this.state.value}
placeholder="Search the schema ..."
/>
</label>
}
</div>
);
}

handleChange = event => {
this.setState({ value: event.target.value });
this._debouncedOnSearch();
}
}
135 changes: 135 additions & 0 deletions src/components/DocExplorer/SearchResults.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/**
* Copyright (c) 2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE-examples file in the root directory of this source tree.
*/

import React, { PropTypes } from 'react';

import TypeLink from './TypeLink';

export default class SearchResults extends React.Component {
static propTypes = {
schema: PropTypes.object,
searchValue: PropTypes.string,
onClickType: PropTypes.func,
onClickField: PropTypes.func,
}

shouldComponentUpdate(nextProps) {
return this.props.schema !== nextProps.schema ||
this.props.searchValue !== nextProps.searchValue;
}

render() {
const searchValue = this.props.searchValue;
const schema = this.props.schema;
const onClickType = this.props.onClickType;
const onClickField = this.props.onClickField;

const typeMap = schema.getTypeMap();

const matchedTypes = [];
const matchedFields = [];

const typeNames = Object.keys(typeMap);
for (const typeName of typeNames) {
if (matchedTypes.length + matchedFields.length >= 100) {
break;
}

const type = typeMap[typeName];
const matchedOn = [];
if (this._isMatch(typeName, searchValue)) {
matchedOn.push('Type Name');
}

if (matchedOn.length) {
matchedTypes.push(
<div className="doc-category-item">
<TypeLink type={type} onClick={onClickType} />
</div>
);
}

if (type.getFields) {
const fields = type.getFields();
Object.keys(fields).forEach(fieldName => {
const field = fields[fieldName];
if (this._isMatch(fieldName, searchValue)) {
matchedFields.push(
<div className="doc-category-item">
<a className="field-name"
onClick={event => onClickField(field, type, event)}>
{field.name}
</a>
{' on '}
<TypeLink type={type} onClick={onClickType} />
</div>
);
} else if (field.args && field.args.length) {
const matches =
field.args.filter(arg => this._isMatch(arg.name, searchValue));
if (matches.length > 0) {
matchedFields.push(
<div className="doc-category-item">
<a className="field-name"
onClick={event => onClickField(field, type, event)}>
{field.name}
</a>
{'('}
<span>
{matches.map(arg =>
<span className="arg" key={arg.name}>
<span className="arg-name">{arg.name}</span>
{': '}
<TypeLink type={arg.type} onClick={onClickType} />
</span>
)}
</span>
{')'}
{' on '}
<TypeLink type={type} onClick={onClickType} />
</div>
);
}
}
});
}
}

if (matchedTypes.length === 0 && matchedFields.length === 0) {
return (
<span className="doc-alert-text">
{'No results found.'}
</span>
);
}

return (
<div>
<div className="doc-category">
{
(matchedTypes.length > 0 || matchedFields.length > 0) &&
<div className="doc-category-title">
{'search results'}
</div>
}
{matchedTypes}
{matchedFields}
</div>
</div>
);
}

_isMatch(sourceText, searchValue) {
try {
const escaped = searchValue.replace(/[^_0-9A-Za-z]/g, ch => '\\' + ch);
return sourceText.search(new RegExp(escaped, 'i')) !== -1;
} catch (e) {
return sourceText.toLowerCase().indexOf(searchValue.toLowerCase()) !== -1;
}
}
}
Loading