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

WIP: markdown to custom components #90

Closed
wants to merge 11 commits into from
20 changes: 10 additions & 10 deletions packages/docs-site/docs-consolidator.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ const { join } = require('path')
const matter = require('gray-matter')
const chalk = require('chalk')
const rimraf = require('rimraf')
const {kebabCase} = require('lodash')
const { kebabCase } = require('lodash')
const { execSync } = require('child_process')
const package = require('./package.json')
const manifest = require('./package.json')
const { convertToMDX } = require('./readme-preparser')

// A list of packages to check
const packages = package.packages
const { packages } = manifest

// The original docs from the 'documentation' package
const originalDocsFolder = resolve(__dirname, '../documentation/library')
Expand Down Expand Up @@ -57,9 +58,7 @@ const returnFinalData = (data, componentData) => {
const docs = componentData
.map(cd => {
if (exclusions(match).includes(cd.package.toLowerCase())) return
return `\n<implementation type="${cd.package}">\n${
cd.content
}\n</implementation>\n`
return `\n<implementation type="${cd.package}">\n${cd.content}\n</implementation>\n`
})
.join('')
return data.replace(regex, match[1] + docs + match[3])
Expand Down Expand Up @@ -92,7 +91,6 @@ const accumulatedData = []
* @param {String} componentsFolder : The path to the components folder of the package
* @param {String} package : The name of the package
*/

const componentLoop = (componentsFolder, package) => {
const components = fs.readdirSync(componentsFolder)
return components.forEach(component => {
Expand All @@ -106,11 +104,14 @@ const componentLoop = (componentsFolder, package) => {
const fileContent = matter(
fs.readFileSync(resolve(componentFolder, file), 'utf8')
)

const mdx = convertToMDX(fileContent.content)

console.group(`⚙️ Generating ${chalk.yellow(component)} docs...`)
accumulatedData.push({
package,
component: kebabCase(fileContent.data.title),
content: fileContent.content,
content: mdx,
})
console.log(chalk.green(` ✓ Done`))
console.groupEnd()
Expand Down Expand Up @@ -186,10 +187,9 @@ const matchDocs = (docsPath, doc, prefix) => {
}
}


/**
* folderLoop
* Loops through the component docs folder and reads the files and
* Loops through the component docs folder and reads the files and
* then passes the results to matchDocs
* @param {String} docsPath The full path to the docs folder
* @param {String} prefix The subfolder that the current component readme is in (defaults to false if no prefix is provided)
Expand Down
11 changes: 4 additions & 7 deletions packages/docs-site/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@
"jest-dom": "^3.1.1",
"lint-staged": "^8.2.1",
"lodash": "^4.17.11",
"marksy": "^8.0.0",
"mdtable2json": "^0.1.0",
"node-fs-extra": "^0.8.2",
"normalize-scss": "^7.0.1",
"prettier": "^1.16.4",
Expand Down Expand Up @@ -96,19 +98,14 @@
"url": "https://github.com/Royal-Navy/royalnavy.io/issues"
},
"packages": [
{
"source": "vue-component-library",
"name": "Vue",
"componentPath": "/src/components"
},
{
"source": "react-component-library",
"name": "React",
"componentPath": "/src/components"
},
{
"source": "html-storybook",
"name": "HTML",
"source": "vue-component-library",
"name": "Vue",
"componentPath": "/src/components"
}
],
Expand Down
67 changes: 67 additions & 0 deletions packages/docs-site/readme-preparser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
const { marksy } = require('marksy')
const { getTables } = require('mdtable2json')
const ReactDOMServer = require('react-dom/server')
const { createElement } = require('react')

/**
* Transform table markdown AST (rows and cells) into data
* structure that can be consumed by the `<DataTable />` component
*
* NOTE: The implementation is limited to 1 table per `README.md`
*
* @param {array} children
* @returns {array}
*/
function transformTableData(markdown) {
const tables = getTables(markdown)

return (tables.length >= 1 && tables[0].json) || []
}

/**
* Base64 encode a prop so that it doesn't confuse the MDX parser
*
* @param {string} string
* @returns {string}
*/
function encodeProp(string) {
return Buffer.from(string).toString('base64')
}

/**
* Generates a compiler to create an MDX version of a `README.md`
*
* TODO: Use JSX instead of createElement and transpile at runtime
*
* @param {string} markdown
* @param {object} options
* @returns {function}
*/
function compile(markdown, options) {
const tableData = transformTableData(markdown)

return marksy({
createElement,
elements: {
code({ language, code }) {
return createElement('CodeHighlighter', {
example: encodeProp(code),
source: encodeProp(code),
language: language || 'javascript',
})
},
table() {
return createElement('DataTable', {
caption: 'Props',
data: encodeProp(JSON.stringify(tableData)),
})
},
},
})(markdown, options)
}

exports.convertToMDX = markdown => {
const { tree } = compile(markdown, {})

return ReactDOMServer.renderToStaticMarkup(tree)
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable react/no-danger */
import React, { useEffect, useState } from 'react'
import PropTypes from 'prop-types'

Expand All @@ -19,7 +20,7 @@ const CodeHighlighter = ({ example, source, language }) => {
function copyToClipboard() {
const textarea = document.createElement('textarea')

textarea.innerText = source
textarea.innerText = atob(source)
document.body.appendChild(textarea)
textarea.select()
document.execCommand('copy')
Expand All @@ -31,7 +32,7 @@ const CodeHighlighter = ({ example, source, language }) => {
return (
<article className="code-highlighter">
<header className="code-highlighter__head" data-testid="example">
{example}
<div dangerouslySetInnerHTML={{ __html: atob(example) }} />
</header>
<section className="code-highlighter__body">
{typeof document !== 'undefined' &&
Expand All @@ -48,7 +49,7 @@ const CodeHighlighter = ({ example, source, language }) => {
)}
<div className="code-highlighter__source">
<pre className="line-numbers">
<code className={`language-${language}`}>{`${source}`}</code>
<code className={`language-${language}`}>{atob(source)}</code>
</pre>
</div>
</section>
Expand All @@ -57,7 +58,7 @@ const CodeHighlighter = ({ example, source, language }) => {
}

CodeHighlighter.propTypes = {
example: PropTypes.instanceOf(Object),
example: PropTypes.string,
source: PropTypes.string,
language: PropTypes.string,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ describe('CodeHighlighter', () => {
codehighlighter = render(
<CodeHighlighter
language="javascript"
example={<h1>This is some example JSX</h1>}
example="This is some example JSX"
source="function restructureNodes(nodes) { return nodes.map(node => {}) }"
/>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ function extractHeadings(data) {
}

const DataTable = ({ data, caption }) => {
const [tableData, setTableData] = useState(data)
const [tableData, setTableData] = useState(JSON.parse(atob(data)))

const sortByColumn = useCallback(
e => {
const column = e.target.getAttribute('data-column')
setTableData(sortBy(data, column))
setTableData(sortBy(JSON.parse(atob(data)), column))
},
[tableData]
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import TableRow from './table-row'
const TableBody = ({ rows }) => {
return (
<tbody className="data-table__body">
{rows.map(row => {
return <TableRow key={uuid()} cells={row} />
})}
{rows.length >= 1 &&
rows.map(row => {
return <TableRow key={uuid()} cells={row} />
})}
</tbody>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const PostArticle = ({ mdx, className, children }) => {

PostArticle.propTypes = {
className: PropTypes.string,
mdx: PropTypes.instanceOf(Object).isRequired,
mdx: PropTypes.string.isRequired,
children: PropTypes.instanceOf(Array),
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.post-article {
width: 0;
Copy link
Member Author

@m7kvqbe1 m7kvqbe1 Jun 26, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a temporary layout fix (hack). The post-article component needs styling from the ground up.

}
7 changes: 7 additions & 0 deletions packages/documentation/library/pages/components/button.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@ title: Button
description: Buttons are one of the foundational elements of any application.
---

<!--
Imports are relative from within the docs-site codebase (post-import).
Imports must be included below any Front Matter.
-->
import CodeHighlighter from '../../../components/presenters/code-highlighter'
import DataTable from '../../../components/presenters/data-table'

# Overview

Buttons are a key component in any application. Establishing hierarchy between actions is crucial to ensuring an interface is easy to understand by a user. In the NELSON component library, we provide 3 tiers of buttons.
Expand Down