1
0
mirror of https://github.com/oceanprotocol/docs.git synced 2024-11-26 19:49:26 +01:00

refactor API reference templates, split up long template files

This commit is contained in:
Matthias Kretschmann 2019-01-22 22:06:06 +01:00
parent eaadda98a8
commit b89b7bddae
Signed by: m
GPG Key ID: 606EEEF3C479A91F
17 changed files with 578 additions and 550 deletions

View File

@ -153,7 +153,7 @@ exports.createPages = ({ graphql, actions }) => {
// Create pages from swagger json files
//
const apiSwaggerTemplate = path.resolve(
'./src/templates/ApiSwagger.jsx'
'./src/templates/Swagger/index.jsx'
)
const petStoreSlug = '/references/petstore/'
@ -202,7 +202,7 @@ exports.createPages = ({ graphql, actions }) => {
//
const typeDocSpecs = ['./data/squid-js.json']
const typedocTemplate = path.resolve(
'./src/templates/Typedoc.jsx'
'./src/templates/Typedoc/index.jsx'
)
typeDocSpecs.forEach(spec => {

View File

@ -73,6 +73,7 @@
"eslint-plugin-prettier": "^3.0.0",
"markdownlint-cli": "^0.13.0",
"npm-run-all": "^4.1.5",
"ora": "^3.0.0",
"prettier": "^1.15.3",
"prettier-stylelint": "^0.4.2",
"stylelint": "^9.9.0",

View File

@ -1,10 +1,11 @@
#!/usr/bin/env node
/* eslint-disable no-console, security/detect-child-process */
/* eslint-disable no-console, security/detect-child-process, security/detect-non-literal-fs-filename */
const fs = require('fs')
const typedoc = require('typedoc')
const typescript = require('typescript')
const ora = require('ora')
const squidJsPackage = require('../external/squid-js/package.json')
const { exec } = require('child_process')
@ -21,17 +22,18 @@ const config = typescript.findConfigFile(
)
// npm install for squid-js
console.log('Installing submodule dependencies...')
const spinnerNpm = ora('Installing submodule dependencies...').start()
const install = exec(
'cd ./external/squid-js && npm i && git checkout package-lock.json'
)
install.on('exit', () => {
spinnerNpm.succeed('Installed submodule dependencies.')
generateJson()
})
const generateJson = () => {
console.log('Generating TypeDoc json...')
const spinnerTypedoc = ora('Generating TypeDoc json...').start()
// Setup our TypeDoc app
const app = new typedoc.Application({
@ -45,7 +47,7 @@ const generateJson = () => {
app.generateJson(project, outPath)
// Parse and modify json output
const jsonOrig = JSON.parse(fs.readFileSync(outPath, 'utf8')) // eslint-disable-line
const jsonOrig = JSON.parse(fs.readFileSync(outPath, 'utf8'))
const jsonFinal = {
info: {
@ -58,5 +60,7 @@ const generateJson = () => {
...jsonOrig
}
fs.writeFileSync(outPath, JSON.stringify(jsonFinal, null, 4)) // eslint-disable-line
fs.writeFileSync(outPath, JSON.stringify(jsonFinal, null, 4))
spinnerTypedoc.succeed('Generated TypeDoc json.')
}

View File

@ -1,321 +0,0 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { graphql } from 'gatsby'
import Helmet from 'react-helmet'
import slugify from 'slugify'
import Layout from '../components/Layout'
import Content from '../components/Content'
import HeaderSection from '../components/HeaderSection'
import Sidebar from '../components/Sidebar'
import DocHeader from '../components/DocHeader'
import DocFooter from '../components/DocFooter'
import SEO from '../components/Seo'
import stylesDoc from './Doc.module.scss'
import styles from './ApiSwagger.module.scss'
const cleanKey = key => {
let keyCleaned = key
if (key.includes('aquarius')) {
keyCleaned = key.replace(/\/api\/v1\/aquarius/gi, '')
}
if (key.includes('brizo')) {
keyCleaned = key.replace(/\/api\/v1\/brizo/gi, '')
}
return keyCleaned
}
const toc = api => {
const items = Object.keys(api.paths).map(
key => `<li key=${key}>
<a href="#${slugify(cleanKey(key))}"><code>${cleanKey(
key
)}</code></a>
</li>`
)
return `<ul>${items}</ul>`
}
const SwaggerMeta = ({ contact, license }) => (
<ul className={styles.swaggerMeta}>
{contact && (
<li>
<a href={`mailto:${contact.email}`}>{contact.email}</a>
</li>
)}
{license && (
<li>
<a href={license.url}>{license.name}</a>
</li>
)}
</ul>
)
SwaggerMeta.propTypes = {
contact: PropTypes.object,
license: PropTypes.object
}
const ParameterExample = ({ properties }) => (
//
// HEADS UP!
//
// Constructing the example request body here based on the defined properties
// where `key` is the name of the property, and `properties[key].example` is
// the value for it.
//
// Making prism.js pick up on the strings only output didn't work out so well
// so the spans and classes this plugin would add are added manually here. Since we
// include the prism css file globally this is picked up by that.
//
// But this can only work if all keys and values are manually constructed here, which
// is almost impossible to do for deep nested objects or arrays as example value. Running
// that code through `JSON.stringify` won't syntax highlight that part of the code.
//
<pre className="language-json">
<code className="language-json">
{'{'}
{properties &&
Object.keys(properties).map(key => (
<div key={key}>
<span className="token property">{` "${key}"`}</span>
<span className="token operator">{`: `}</span>
{properties[key].type === 'string' && (
<span className="token string">{`"${
properties[key].example
}"`}</span>
)}
{(properties[key].type === 'integer' ||
properties[key].type === 'number') && (
<span className="token number">
{`${properties[key].example}`}
</span>
)}
{(properties[key].type === 'array' ||
properties[key].type === 'object') &&
JSON.stringify(properties[key].example, null, 2)}
,
</div>
))}
{'}'}
</code>
</pre>
)
ParameterExample.propTypes = {
properties: PropTypes.object
}
const Parameters = ({ parameters }) => (
<>
<h4 className={styles.subHeading}>Parameters</h4>
{parameters.map(parameter => {
const { name, type, required, description, schema } = parameter
return (
<div className={styles.parameters} key={parameter.name}>
<h5>
<code>{name}</code>
{required && (
<span
className={styles.parameterRequired}
title="required parameter"
>
*
</span>
)}
<span className={styles.parameterType}>{type}</span>
</h5>
<p>{description}</p>
{schema && (
<ParameterExample properties={schema.properties} />
)}
</div>
)
})}
</>
)
const Responses = ({ responses }) => (
<>
<h4 className={styles.subHeading}>Responses</h4>
{Object.keys(responses).map(key => (
<div key={key} className={styles.response}>
<code>{key}</code> {responses[key].description}
</div>
))}
</>
)
const Method = ({ keyName, value }) => {
const { summary, description, parameters, responses } = value
return (
<div className={styles.method}>
<h3 className={styles.pathMethod} data-type={keyName}>
{keyName}
</h3>
<p>{summary}</p>
{description && <p>{description}</p>}
{/*
{consumes &&
consumes.map((item, i) => (
<div key={i}>
<code>{item}</code>
</div>
))}
*/}
{parameters && parameters.length && (
<Parameters parameters={parameters} />
)}
{responses && Object.keys(responses).length !== 0 && (
<Responses responses={responses} />
)}
</div>
)
}
Method.propTypes = {
keyName: PropTypes.string,
value: PropTypes.object
}
const Paths = ({ paths }) =>
Object.entries(paths).map(([key, value]) => (
<div key={key}>
<h2 id={slugify(cleanKey(key))} className={styles.pathName}>
<code>{cleanKey(key)}</code>
</h2>
{Object.entries(value).map(([key, value]) => (
<Method key={key} keyName={key} value={value} />
))}
</div>
))
const BasePath = ({ host, basePath }) => (
<div className={styles.basePath}>
<h2>Base Path</h2>
<code>
<span>{host}</span>
{basePath}
</code>
</div>
)
BasePath.propTypes = {
host: PropTypes.string,
basePath: PropTypes.string
}
export default class ApiSwaggerTemplate extends Component {
static propTypes = {
data: PropTypes.object.isRequired,
location: PropTypes.object.isRequired,
pageContext: PropTypes.object.isRequired
}
render() {
const { location, data, pageContext } = this.props
const sections = data.allSectionsYaml.edges
const { api } = pageContext
const { host, basePath, info, paths } = api
const { title, description, version, license, contact } = info
// output section title as defined in sections.yml
const sectionTitle = sections.map(({ node }) => {
// compare section against section title from sections.yml
if (node.title.toLowerCase().includes('references')) {
return node.title
}
})
return (
<>
<Helmet>
<body className={'references'} />
</Helmet>
<SEO
title={title}
description={description}
slug={pageContext.slug}
article
/>
<Layout location={location}>
<HeaderSection title={sectionTitle} />
<Content>
<main className={stylesDoc.wrapper}>
<aside className={stylesDoc.sidebar}>
<Sidebar
location={location}
sidebar={'references'}
collapsed
toc
tableOfContents={toc(api)
.split(',')
.join('')}
/>
</aside>
<article className={stylesDoc.main}>
<DocHeader
title={title}
description={description}
prepend={
<span className={styles.version}>
{version}
</span>
}
/>
{(contact || license) && (
<SwaggerMeta
contact={contact}
license={license}
/>
)}
{(host || basePath) && (
<BasePath host={host} basePath={basePath} />
)}
<Paths paths={paths} />
<DocFooter
url={`https://github.com/oceanprotocol/${title.toLowerCase()}`}
externalName={`${title} Swagger spec`}
/>
</article>
</main>
</Content>
</Layout>
</>
)
}
}
export const apiSwaggerQuery = graphql`
query {
allSectionsYaml {
edges {
node {
title
description
link
}
}
}
}
`

View File

@ -38,22 +38,25 @@ export default class DocTemplate extends Component {
location: PropTypes.object.isRequired
}
// output section title as defined in sections.yml
sectionTitle = this.props.data.allSectionsYaml.edges.map(({ node }) => {
// compare section against section title from sections.yml
if (
node.title
.toLowerCase()
.includes(this.props.data.markdownRemark.fields.section)
) {
return node.title
}
})
render() {
const { location } = this.props
const post = this.props.data.markdownRemark
const sections = this.props.data.allSectionsYaml.edges
const { section, slug } = post.fields
const { title, description } = post.frontmatter
const { tableOfContents } = post
// output section title as defined in sections.yml
const sectionTitle = sections.map(({ node }) => {
// compare section against section title from sections.yml
if (node.title.toLowerCase().includes(section)) {
return node.title
}
})
const isApiSection = location.pathname.includes('/references/')
return (
@ -70,7 +73,9 @@ export default class DocTemplate extends Component {
/>
<Layout location={location}>
<HeaderSection title={section ? sectionTitle : title} />
<HeaderSection
title={section ? this.sectionTitle : title}
/>
<Content>
{section ? (

View File

@ -48,3 +48,9 @@
max-width: 73%;
margin: auto;
}
.version {
font-size: $font-size-base;
font-family: $font-family-monospace;
color: $brand-grey-light;
}

View File

@ -0,0 +1,151 @@
import React from 'react'
import PropTypes from 'prop-types'
import slugify from 'slugify'
import { cleanPathKey } from './utils'
import styles from './Paths.module.scss'
const ParameterExample = ({ properties }) => (
//
// HEADS UP!
//
// Constructing the example request body here based on the defined properties
// where `key` is the name of the property, and `properties[key].example` is
// the value for it.
//
// Making prism.js pick up on the strings only output didn't work out so well
// so the spans and classes this plugin would add are added manually here. Since we
// include the prism css file globally this is picked up by that.
//
// But this can only work if all keys and values are manually constructed here, which
// is almost impossible to do for deep nested objects or arrays as example value. Running
// that code through `JSON.stringify` won't syntax highlight that part of the code.
//
<pre className="language-json">
<code className="language-json">
{'{'}
{properties &&
Object.keys(properties).map(key => (
<div key={key}>
<span className="token property">{` "${key}"`}</span>
<span className="token operator">{`: `}</span>
{properties[key].type === 'string' && (
<span className="token string">{`"${
properties[key].example
}"`}</span>
)}
{(properties[key].type === 'integer' ||
properties[key].type === 'number') && (
<span className="token number">
{`${properties[key].example}`}
</span>
)}
{(properties[key].type === 'array' ||
properties[key].type === 'object') &&
JSON.stringify(properties[key].example, null, 2)}
,
</div>
))}
{'}'}
</code>
</pre>
)
ParameterExample.propTypes = {
properties: PropTypes.object
}
const Parameters = ({ parameters }) => (
<>
<h4 className={styles.subHeading}>Parameters</h4>
{parameters.map(parameter => {
const { name, type, required, description, schema } = parameter
return (
<div className={styles.parameters} key={parameter.name}>
<h5>
<code>{name}</code>
{required && (
<span
className={styles.parameterRequired}
title="required parameter"
>
*
</span>
)}
<span className={styles.parameterType}>{type}</span>
</h5>
<p>{description}</p>
{schema && (
<ParameterExample properties={schema.properties} />
)}
</div>
)
})}
</>
)
const Responses = ({ responses }) => (
<>
<h4 className={styles.subHeading}>Responses</h4>
{Object.keys(responses).map(key => (
<div key={key} className={styles.response}>
<code>{key}</code> {responses[key].description}
</div>
))}
</>
)
const Method = ({ keyName, value }) => {
const { summary, description, parameters, responses } = value
return (
<div className={styles.method}>
<h3 className={styles.pathMethod} data-type={keyName}>
{keyName}
</h3>
<p>{summary}</p>
{description && <p>{description}</p>}
{/*
{consumes &&
consumes.map((item, i) => (
<div key={i}>
<code>{item}</code>
</div>
))}
*/}
{parameters && parameters.length && (
<Parameters parameters={parameters} />
)}
{responses && Object.keys(responses).length !== 0 && (
<Responses responses={responses} />
)}
</div>
)
}
Method.propTypes = {
keyName: PropTypes.string,
value: PropTypes.object
}
const Paths = ({ paths }) =>
Object.entries(paths).map(([key, value]) => (
<div key={key}>
<h2 id={slugify(cleanPathKey(key))} className={styles.pathName}>
<code>{cleanPathKey(key)}</code>
</h2>
{Object.entries(value).map(([key, value]) => (
<Method key={key} keyName={key} value={value} />
))}
</div>
))
export default Paths

View File

@ -1,52 +1,5 @@
@import 'variables';
.version {
font-size: $font-size-base;
font-family: $font-family-monospace;
color: $brand-grey-light;
}
.swaggerMeta {
margin-top: -($spacer);
padding: 0;
font-size: $font-size-small;
li {
display: inline-block;
margin-left: $spacer;
&:first-child {
margin-left: 0;
}
&:before {
display: none;
}
}
}
.basePath {
margin-top: $spacer;
h2 {
font-size: $font-size-h3;
border-bottom: 1px solid $brand-grey-lighter;
padding-bottom: $spacer / 2;
margin-top: $spacer * 2;
margin-bottom: $spacer;
}
span {
color: $brand-grey-light;
}
code {
// stylelint-disable-next-line
font-size: $font-size-large !important;
font-weight: $font-weight-bold;
}
}
.pathName {
font-size: $font-size-h3;
border-bottom: 1px solid $brand-grey-lighter;

View File

@ -0,0 +1,19 @@
import slugify from 'slugify'
import { cleanPathKey } from './utils'
const Toc = api => {
const items = Object.keys(api.paths)
.map(
key =>
`<li key=${key}>
<a href="#${slugify(cleanPathKey(key))}">
<code>${cleanPathKey(key)}</code>
</a>
</li>`
)
.join('')
return `<ul>${items}</ul>`
}
export default Toc

View File

@ -0,0 +1,152 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { graphql } from 'gatsby'
import Helmet from 'react-helmet'
import Layout from '../../components/Layout'
import Content from '../../components/Content'
import HeaderSection from '../../components/HeaderSection'
import Sidebar from '../../components/Sidebar'
import DocHeader from '../../components/DocHeader'
import DocFooter from '../../components/DocFooter'
import SEO from '../../components/Seo'
import Toc from './Toc'
import Paths from './Paths'
import stylesDoc from '../Doc.module.scss'
import styles from './index.module.scss'
const SwaggerMeta = ({ contact, license }) => (
<ul className={styles.swaggerMeta}>
{contact && (
<li>
<a href={`mailto:${contact.email}`}>{contact.email}</a>
</li>
)}
{license && (
<li>
<a href={license.url}>{license.name}</a>
</li>
)}
</ul>
)
SwaggerMeta.propTypes = {
contact: PropTypes.object,
license: PropTypes.object
}
const BasePath = ({ host, basePath }) => (
<div className={styles.basePath}>
<h2>Base Path</h2>
<code>
<span>{host}</span>
{basePath}
</code>
</div>
)
BasePath.propTypes = {
host: PropTypes.string,
basePath: PropTypes.string
}
export default class ApiSwaggerTemplate extends Component {
static propTypes = {
data: PropTypes.object.isRequired,
location: PropTypes.object.isRequired,
pageContext: PropTypes.object.isRequired
}
// output section title as defined in sections.yml
sectionTitle = this.props.data.allSectionsYaml.edges.map(({ node }) => {
// compare section against section title from sections.yml
if (node.title.toLowerCase().includes('references')) {
return node.title
}
})
render() {
const { location, pageContext } = this.props
const { api } = pageContext
const { host, basePath, info, paths } = api
const { title, description, version, license, contact } = info
return (
<>
<Helmet>
<body className={'references'} />
</Helmet>
<SEO
title={title}
description={description}
slug={pageContext.slug}
article
/>
<Layout location={location}>
<HeaderSection title={this.sectionTitle} />
<Content>
<main className={stylesDoc.wrapper}>
<aside className={stylesDoc.sidebar}>
<Sidebar
location={location}
sidebar={'references'}
collapsed
toc
tableOfContents={Toc(api)}
/>
</aside>
<article className={stylesDoc.main}>
<DocHeader
title={title}
description={description}
prepend={
<span className={stylesDoc.version}>
{version}
</span>
}
/>
{(contact || license) && (
<SwaggerMeta
contact={contact}
license={license}
/>
)}
{(host || basePath) && (
<BasePath host={host} basePath={basePath} />
)}
<Paths paths={paths} />
<DocFooter
url={`https://github.com/oceanprotocol/${title.toLowerCase()}`}
externalName={`${title} Swagger spec`}
/>
</article>
</main>
</Content>
</Layout>
</>
)
}
}
export const apiSwaggerQuery = graphql`
query {
allSectionsYaml {
edges {
node {
title
description
link
}
}
}
}
`

View File

@ -0,0 +1,42 @@
@import 'variables';
.swaggerMeta {
margin-top: -($spacer);
padding: 0;
font-size: $font-size-small;
li {
display: inline-block;
margin-left: $spacer;
&:first-child {
margin-left: 0;
}
&:before {
display: none;
}
}
}
.basePath {
margin-top: $spacer;
h2 {
font-size: $font-size-h3;
border-bottom: 1px solid $brand-grey-lighter;
padding-bottom: $spacer / 2;
margin-top: $spacer * 2;
margin-bottom: $spacer;
}
span {
color: $brand-grey-light;
}
code {
// stylelint-disable-next-line
font-size: $font-size-large !important;
font-weight: $font-weight-bold;
}
}

View File

@ -0,0 +1,13 @@
export const cleanPathKey = key => {
let keyCleaned = key
if (key.includes('aquarius')) {
keyCleaned = key.replace(/\/api\/v1\/aquarius/gi, '')
}
if (key.includes('brizo')) {
keyCleaned = key.replace(/\/api\/v1\/brizo/gi, '')
}
return keyCleaned
}

View File

@ -1,50 +1,11 @@
import React, { Component } from 'react'
import React from 'react'
import PropTypes from 'prop-types'
import { graphql } from 'gatsby'
import Helmet from 'react-helmet'
import slugify from 'slugify'
import Layout from '../components/Layout'
import Content from '../components/Content'
import HeaderSection from '../components/HeaderSection'
import Sidebar from '../components/Sidebar'
import DocHeader from '../components/DocHeader'
import SEO from '../components/Seo'
import stylesDoc from './Doc.module.scss'
import styles from './Typedoc.module.scss'
import styles from './Entities.module.scss'
// more kinds: 'Property', 'Class'
const showKindOfProperty = ['Method', 'Property']
const toc = typedoc => {
const items = typedoc
.map(({ name, children }) => {
const parentName = name
return `
<li>
<a href="#${slugify(name)}"><code>
${name}
</code></a>
<ul>
${children
.map(
({ name }) =>
`<li key={name}>
<a href="#${parentName}-${slugify(name)}">
<code>${name}</code>
</a>
</li>`
)
.join('')}
</ul>
</li>
`
})
.join('')
return `<ul>${items}</ul>`
}
const Type = ({ type }) => {
let isArray = false
if (type.type === 'array') {
@ -116,6 +77,7 @@ const MethodDetails = ({ property }) => {
const { isOptional } = flags
const description =
comment && (comment.text || comment.shortText)
return (
<div
className={styles.parameters}
@ -250,7 +212,7 @@ PropertyWrapper.propTypes = {
parentAnchor: PropTypes.string
}
const Entity = ({ entities, sourceUrl }) =>
const Entities = ({ entities, sourceUrl }) =>
entities.map(({ name, comment, children }) => (
<div key={name}>
<h2 id={slugify(name)} className={styles.entityName}>
@ -264,6 +226,9 @@ const Entity = ({ entities, sourceUrl }) =>
)}
{children
// TODO: this filter neeeds to be added in utils.js
// cleanTypedocData function to make content and
// sidebar TOC consistent
.filter(({ kindString }) =>
showKindOfProperty.includes(kindString)
)
@ -278,124 +243,9 @@ const Entity = ({ entities, sourceUrl }) =>
</div>
))
Entity.propTypes = {
Entities.propTypes = {
entities: PropTypes.array.isRequired,
sourceUrl: PropTypes.string
}
export default class TypedocTemplate extends Component {
static propTypes = {
data: PropTypes.object.isRequired,
location: PropTypes.object.isRequired,
pageContext: PropTypes.object.isRequired
}
componentWillMount() {
const { typedoc, classes } = this.props.pageContext
this.setState({
typedocData: this.cleanTypedocData(typedoc, classes)
})
}
cleanTypedocData(data, useClasses) {
const nodes = data.children
const cleanData = nodes
.map(node => ({
...node,
name: node.name.replace(/"/g, ''),
child: node.children && node.children[0]
}))
.filter(({ name }) => (useClasses || []).includes(name))
.sort(
(a, b) =>
useClasses.indexOf(a.name) - useClasses.indexOf(b.name)
)
.map(({ child }) => child)
.map(node => ({
...node,
children: node.children.sort((a, b) => a.id - b.id)
}))
return cleanData
}
render() {
const { location, data, pageContext } = this.props
const { typedocData } = this.state
const sections = data.allSectionsYaml.edges
const { typedoc } = pageContext
const { info } = typedoc
const { title, description, version, sourceUrl } = info
// output section title as defined in sections.yml
const sectionTitle = sections.map(({ node }) => {
// compare section against section title from sections.yml
if (node.title.toLowerCase().includes('references')) {
return node.title
}
})
return (
<>
<Helmet>
<body className={'references'} />
</Helmet>
<SEO
title={title}
description={description}
slug={pageContext.slug}
article
/>
<Layout location={location}>
<HeaderSection title={sectionTitle} />
<Content>
<main className={stylesDoc.wrapper}>
<aside className={stylesDoc.sidebar}>
<Sidebar
location={location}
sidebar={'references'}
collapsed
toc
tableOfContents={toc(typedocData)}
/>
</aside>
<article className={stylesDoc.main}>
<DocHeader
title={title}
description={description}
prepend={
<span className={styles.version}>
{version}
</span>
}
/>
<Entity
entities={typedocData}
sourceUrl={sourceUrl}
/>
</article>
</main>
</Content>
</Layout>
</>
)
}
}
export const TypedocQuery = graphql`
query {
allSectionsYaml {
edges {
node {
title
description
link
}
}
}
}
`
export default Entities

View File

@ -1,11 +1,5 @@
@import 'variables';
.version {
font-size: $font-size-base;
font-family: $font-family-monospace;
color: $brand-grey-light;
}
.entityName {
font-size: $font-size-h2;
border-bottom: 1px solid $brand-grey-lighter;

View File

@ -0,0 +1,33 @@
import slugify from 'slugify'
const Toc = typedoc => {
const subItems = (children, parentName) =>
children
.map(
({ name }) =>
`<li>
<a href="#${parentName}-${slugify(name)}">
<code>${name}</code>
</a>
</li>`
)
.join('')
const items = typedoc
.map(
({ name, children }) =>
`<li>
<a href="#${slugify(name)}">
<code>${name}</code>
</a>
<ul>
${subItems(children, name)}
</ul>
</li>`
)
.join('')
return `<ul>${items}</ul>`
}
export default Toc

View File

@ -0,0 +1,107 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { graphql } from 'gatsby'
import Helmet from 'react-helmet'
import Layout from '../../components/Layout'
import Content from '../../components/Content'
import HeaderSection from '../../components/HeaderSection'
import Sidebar from '../../components/Sidebar'
import DocHeader from '../../components/DocHeader'
import SEO from '../../components/Seo'
import { cleanTypedocData } from './utils'
import Entities from './Entities'
import Toc from './Toc'
import stylesDoc from '../Doc.module.scss'
export default class TypedocTemplate extends Component {
static propTypes = {
data: PropTypes.object.isRequired,
location: PropTypes.object.isRequired,
pageContext: PropTypes.object.isRequired
}
typedocCleaned = cleanTypedocData(
this.props.pageContext.typedoc,
this.props.pageContext.classes
)
// output section title as defined in sections.yml
sectionTitle = this.props.data.allSectionsYaml.edges.map(({ node }) => {
// compare section against section title from sections.yml
if (node.title.toLowerCase().includes('references')) {
return node.title
}
})
render() {
const { location, pageContext } = this.props
const { typedoc } = pageContext
const { info } = typedoc
const { title, description, version, sourceUrl } = info
return (
<>
<Helmet>
<body className={'references'} />
</Helmet>
<SEO
title={title}
description={description}
slug={pageContext.slug}
article
/>
<Layout location={location}>
<HeaderSection title={this.sectionTitle} />
<Content>
<main className={stylesDoc.wrapper}>
<aside className={stylesDoc.sidebar}>
<Sidebar
location={location}
sidebar={'references'}
collapsed
toc
tableOfContents={Toc(this.typedocCleaned)}
/>
</aside>
<article className={stylesDoc.main}>
<DocHeader
title={title}
description={description}
prepend={
<span className={stylesDoc.version}>
{version}
</span>
}
/>
<Entities
entities={this.typedocCleaned}
sourceUrl={sourceUrl}
/>
</article>
</main>
</Content>
</Layout>
</>
)
}
}
export const TypedocQuery = graphql`
query {
allSectionsYaml {
edges {
node {
title
description
link
}
}
}
}
`

View File

@ -0,0 +1,19 @@
export const cleanTypedocData = (data, useClasses) => {
const nodes = data.children
const cleanData = nodes
.map(node => ({
...node,
name: node.name.replace(/"/g, ''),
child: node.children && node.children[0]
}))
.filter(({ name }) => (useClasses || []).includes(name))
.sort((a, b) => useClasses.indexOf(a.name) - useClasses.indexOf(b.name))
.map(({ child }) => child)
.map(node => ({
...node,
children: node.children.sort((a, b) => a.id - b.id)
}))
return cleanData
}