Skip to content

Commit

Permalink
Add map option to typography plugin (#15)
Browse files Browse the repository at this point in the history
* Add custom className map option to typography plugin

* Visit listItem nodes directly, rather than through listNode.children

* Update typography plugin readme

* Add release:pre cmd, bump to expected release

* 3.1.0-canary.0

* Expose typography options in allPlugins

* 3.1.0-canary.1
  • Loading branch information
zchsh authored Jan 5, 2021
1 parent f456139 commit aa85fb4
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 44 deletions.
3 changes: 2 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ module.exports = {
// for easy use of everything at the same time
module.exports.allPlugins = ({
anchorLinks: anchorLinksOptions,
typography: typographyOptions,
includeMarkdown: includeMarkdownOptions,
} = {}) => [
[includeMarkdown, includeMarkdownOptions],
[anchorLinks, anchorLinksOptions],
paragraphCustomAlerts,
typography,
[typography, typographyOptions],
]
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@hashicorp/remark-plugins",
"description": "A potpourri of remark plugins used to process .mdx files",
"version": "3.0.0",
"version": "3.1.0-canary.1",
"author": "Jeff Escalante",
"bugs": "https://github.com/hashicorp/remark-plugins/issues",
"contributors": [
Expand Down Expand Up @@ -40,6 +40,7 @@
"test": "jest --verbose",
"release:patch": "release patch && npm publish",
"release:minor": "release minor && npm publish",
"release:major": "release major && npm publish"
"release:major": "release major && npm publish",
"release:pre": "release pre canary && npm publish --tag=canary"
}
}
40 changes: 38 additions & 2 deletions plugins/typography/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# Heading Type Styles

We use a set of global classes for type styling at HashiCorp. This plugin adds type styles to the appropriate elements so that content looks as intended within rendered markdown blocks without duplicating or extending css.
More details to come via our design system 😀
We use a set of global classes for type styling at HashiCorp. This plugin adds type styles to the appropriate elements so that content looks as intended within rendered markdown blocks without duplicating or extending CSS.

### Input

Expand All @@ -26,3 +25,40 @@ Here is some more stuff...

<p className='g-type-long-body'>Here is some more stuff...</p>
```

### Custom Class Mapping

In rare cases, we may want to map custom `class` attributes onto specific elements. Currently, this plugin supports an `options` object, and `options.map` provides this functionality.

Here is an imagined use case where all possible elements have custom `class` attributes. Any one of these elements can be omitted from the map, and it will fall back to our default `class` for that element.

```js
const options = {
map: {
h1: 'custom-1',
h2: 'custom-2',
h3: 'custom-3',
h4: 'custom-4',
h5: 'custom-5',
h6: 'custom-6',
p: 'custom-paragraph',
li: 'custom-list-item',
},
}
// example use with `mdx`
const output = mdx.sync(fileContents, {
remarkPlugins: [[typographyPlugin, options]],
})
```

With this configuration, and the same input as the previous example, we would expect the following output:

```jsx
<h1 className='custom-1'>Uses</h1>

<p className='custom-paragraph'>Here are some uses...</p>

<h2 className='custom-2'>Another title</h2>

<p className='custom-paragraph'>Here is some more stuff...</p>
```
67 changes: 32 additions & 35 deletions plugins/typography/index.js
Original file line number Diff line number Diff line change
@@ -1,46 +1,43 @@
const visit = require('unist-util-visit')

module.exports = function typographyPlugin() {
module.exports = function typographyPlugin(options = {}) {
function getClassName(elemKey) {
const defaultMap = {
h1: 'g-type-display-2',
h2: 'g-type-display-3',
h3: 'g-type-display-4',
h4: 'g-type-display-5',
h5: 'g-type-display-6',
h6: 'g-type-label',
p: 'g-type-long-body',
li: 'g-type-long-body',
}
const customMap = options.map || {}
return customMap[elemKey] || defaultMap[elemKey]
}

function addClassName(node, className) {
if (!className) return true
const data = node.data || (node.data = {})
const props = data.hProperties || (data.hProperties = {})
data.id = className
props.className = className
}

return function transformer(tree) {
// Add typography classes to headings
visit(tree, 'heading', node => {
const data = node.data || (node.data = {})
const props = data.hProperties || (data.hProperties = {})
visit(tree, 'heading', (node) => {
addClassName(node, getClassName(`h${node.depth}`))
})

if (node.depth === 1) {
data.id = 'g-type-display-2'
props.className = 'g-type-display-2'
}
if (node.depth === 2) {
data.id = 'g-type-display-3'
props.className = 'g-type-display-3'
}
if (node.depth === 3) {
data.id = 'g-type-display-4'
props.className = 'g-type-display-4'
}
if (node.depth === 4) {
data.id = 'g-type-display-5'
props.className = 'g-type-display-5'
}
if (node.depth === 5) {
data.id = 'g-type-display-6'
props.className = 'g-type-display-6'
}
if (node.depth === 6) {
data.id = 'g-type-label'
props.className = 'g-type-label'
}
// Add typography classes to paragraph text
visit(tree, 'paragraph', (node) => {
addClassName(node, getClassName('p'))
})

// Add typography classes to list items
visit(tree, 'list', node => {
node.children.map(li => {
const data = li.data || (li.data = {})
const props = data.hProperties || (data.hProperties = {})
data.id = 'g-type-long-body'
props.className = 'g-type-long-body'
})
visit(tree, 'listItem', (node) => {
addClassName(node, getClassName('li'))
})
}
}
63 changes: 60 additions & 3 deletions plugins/typography/index.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const typeStyles = require('./index.js')
const typographyPlugin = require('./index.js')
const mdx = require('@mdx-js/mdx')

const fileContents = `hi there
Expand All @@ -19,16 +19,73 @@ Foo bar baz wow *amaze*
`

describe('type-styles', () => {
it('should construct add appropriate class names to headings', () => {
const output = mdx.sync(fileContents, { remarkPlugins: [typeStyles] })
it('adds classNames to headings, paragraphs, and list items', () => {
const output = mdx.sync(fileContents, { remarkPlugins: [typographyPlugin] })
expect(output).toMatch(
/<h1 {\.\.\.{\n\s+"className": "g-type-display-2"\n\s+}}>{`Heading One`}<\/h1>/
)
expect(output).toMatch(
/<h2 {\.\.\.{\n\s+"className": "g-type-display-3"\n\s+}}>{`Heading Two`}<\/h2>/
)
expect(output).toMatch(
/<h3 {\.\.\.{\n\s+"className": "g-type-display-4"\n\s+}}>{`Heading Three`}<\/h3>/
)
expect(output).toMatch(
/<h4 {\.\.\.{\n\s+"className": "g-type-display-5"\n\s+}}>{`Heading Four`}<\/h4>/
)
expect(output).toMatch(
/<h5 {\.\.\.{\n\s+"className": "g-type-display-6"\n\s+}}>{`Heading Five`}<\/h5>/
)
expect(output).toMatch(
/<h6 {\.\.\.{\n\s+"className": "g-type-label"\n\s+}}>{`Heading Six`}<\/h6>/
)
expect(output).toMatch(
/<p {\.\.\.{\n\s+"className": "g-type-long-body"\n\s+}}>{`sadklfjhlskdjf`}<\/p>/
)
expect(output).toMatch(
/<li parentName="ul" {\.\.\.{\n\s+"className": "g-type-long-body"\n\s+}}>{`foo`}<\/li>/
)
})

it('allows customization of classNames', () => {
const options = {
map: {
h1: 'custom-1',
h2: 'custom-2',
h3: 'custom-3',
h4: 'custom-4',
h5: 'custom-5',
h6: 'custom-6',
p: 'custom-paragraph',
li: 'custom-list-item',
},
}
const output = mdx.sync(fileContents, {
remarkPlugins: [[typographyPlugin, options]],
})
expect(output).toMatch(
/<h1 {\.\.\.{\n\s+"className": "custom-1"\n\s+}}>{`Heading One`}<\/h1>/
)
expect(output).toMatch(
/<h2 {\.\.\.{\n\s+"className": "custom-2"\n\s+}}>{`Heading Two`}<\/h2>/
)
expect(output).toMatch(
/<h3 {\.\.\.{\n\s+"className": "custom-3"\n\s+}}>{`Heading Three`}<\/h3>/
)
expect(output).toMatch(
/<h4 {\.\.\.{\n\s+"className": "custom-4"\n\s+}}>{`Heading Four`}<\/h4>/
)
expect(output).toMatch(
/<h5 {\.\.\.{\n\s+"className": "custom-5"\n\s+}}>{`Heading Five`}<\/h5>/
)
expect(output).toMatch(
/<h6 {\.\.\.{\n\s+"className": "custom-6"\n\s+}}>{`Heading Six`}<\/h6>/
)
expect(output).toMatch(
/<p {\.\.\.{\n\s+"className": "custom-paragraph"\n\s+}}>{`sadklfjhlskdjf`}<\/p>/
)
expect(output).toMatch(
/<li parentName="ul" {\.\.\.{\n\s+"className": "custom-list-item"\n\s+}}>{`foo`}<\/li>/
)
})
})

0 comments on commit aa85fb4

Please sign in to comment.