Skip to content

Commit 006953d

Browse files
authored
feat(gatsby-remark-autolink-headers): Optionally specify header element types (#23366)
* Add elements prop to options that enables anchor generation for specified header tags only * Update README to reflect addition of elements option * Do not return markdownAST from node callback function in gatsby-remark-autolink-headers * Fix Prettier format issues in gatsby-recipes README
1 parent beb3046 commit 006953d

File tree

5 files changed

+253
-1
lines changed

5 files changed

+253
-1
lines changed

packages/gatsby-recipes/README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ a step
199199

200200
But this won't
201201

202-
<!-- prettier-ignore -->
202+
<!-- prettier-ignore-start -->
203203
```mdx
204204
# Recipes
205205
---
@@ -208,6 +208,7 @@ a step
208208

209209
<File src="something.txt" content="something" />
210210
```
211+
<!-- prettier-ignore-end -->
211212

212213
### Q) What kind of recipe should I write?
213214

packages/gatsby-remark-autolink-headers/README.md

+2
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ Note: if you are using `gatsby-remark-prismjs`, make sure that it’s listed aft
5959
- `removeAccents`: Boolean. Remove accents from generated headings IDs (optional)
6060
- `enableCustomId`: Boolean. Enable custom header IDs with `{#id}` (optional)
6161
- `isIconAfterHeader`: Boolean. Enable the anchor icon to be inline at the end of the header text (optional)
62+
- `elements`: String array. Specify which type of header tags to link (optional)
6263

6364
```javascript
6465
// In your gatsby-config.js
@@ -77,6 +78,7 @@ module.exports = {
7778
maintainCase: true,
7879
removeAccents: true,
7980
isIconAfterHeader: true,
81+
elements: [`h1`, `h4`],
8082
},
8183
},
8284
],

packages/gatsby-remark-autolink-headers/src/__tests__/__snapshots__/index.js.snap

+145
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,73 @@ Object {
250250
}
251251
`;
252252

253+
exports[`gatsby-remark-autolink-headers adds id to a markdown heading when elements prop is passed with matching heading type 1`] = `
254+
Object {
255+
"children": Array [
256+
Object {
257+
"children": Array [],
258+
"data": Object {
259+
"hChildren": Array [
260+
Object {
261+
"type": "raw",
262+
"value": "<svg aria-hidden=\\"true\\" focusable=\\"false\\" height=\\"16\\" version=\\"1.1\\" viewBox=\\"0 0 16 16\\" width=\\"16\\"><path fill-rule=\\"evenodd\\" d=\\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\\"></path></svg>",
263+
},
264+
],
265+
"hProperties": Object {
266+
"aria-label": "heading uno permalink",
267+
"class": "anchor before",
268+
},
269+
},
270+
"title": null,
271+
"type": "link",
272+
"url": "#heading-uno",
273+
},
274+
Object {
275+
"position": Position {
276+
"end": Object {
277+
"column": 14,
278+
"line": 1,
279+
"offset": 13,
280+
},
281+
"indent": Array [],
282+
"start": Object {
283+
"column": 3,
284+
"line": 1,
285+
"offset": 2,
286+
},
287+
},
288+
"type": "text",
289+
"value": "Heading Uno",
290+
},
291+
],
292+
"data": Object {
293+
"hProperties": Object {
294+
"id": "heading-uno",
295+
"style": "position:relative;",
296+
},
297+
"htmlAttributes": Object {
298+
"id": "heading-uno",
299+
},
300+
"id": "heading-uno",
301+
},
302+
"depth": 1,
303+
"position": Position {
304+
"end": Object {
305+
"column": 14,
306+
"line": 1,
307+
"offset": 13,
308+
},
309+
"indent": Array [],
310+
"start": Object {
311+
"column": 1,
312+
"line": 1,
313+
"offset": 0,
314+
},
315+
},
316+
"type": "heading",
317+
}
318+
`;
319+
253320
exports[`gatsby-remark-autolink-headers adds places anchor after header when isIconAfterHeader prop is passed 1`] = `
254321
Object {
255322
"children": Array [
@@ -317,6 +384,84 @@ Object {
317384
}
318385
`;
319386

387+
exports[`gatsby-remark-autolink-headers does not add data to a markdown heading when elements prop is passed with no matching heading type 1`] = `
388+
Object {
389+
"children": Array [
390+
Object {
391+
"position": Position {
392+
"end": Object {
393+
"column": 14,
394+
"line": 1,
395+
"offset": 13,
396+
},
397+
"indent": Array [],
398+
"start": Object {
399+
"column": 3,
400+
"line": 1,
401+
"offset": 2,
402+
},
403+
},
404+
"type": "text",
405+
"value": "Heading Uno",
406+
},
407+
],
408+
"depth": 1,
409+
"position": Position {
410+
"end": Object {
411+
"column": 14,
412+
"line": 1,
413+
"offset": 13,
414+
},
415+
"indent": Array [],
416+
"start": Object {
417+
"column": 1,
418+
"line": 1,
419+
"offset": 0,
420+
},
421+
},
422+
"type": "heading",
423+
}
424+
`;
425+
426+
exports[`gatsby-remark-autolink-headers does not add data to a markdown heading with custom id when elements prop is passed with no matching heading type 1`] = `
427+
Object {
428+
"children": Array [
429+
Object {
430+
"position": Position {
431+
"end": Object {
432+
"column": 27,
433+
"line": 1,
434+
"offset": 26,
435+
},
436+
"indent": Array [],
437+
"start": Object {
438+
"column": 3,
439+
"line": 1,
440+
"offset": 2,
441+
},
442+
},
443+
"type": "text",
444+
"value": "Heading Uno {#custom_h1}",
445+
},
446+
],
447+
"depth": 1,
448+
"position": Position {
449+
"end": Object {
450+
"column": 27,
451+
"line": 1,
452+
"offset": 26,
453+
},
454+
"indent": Array [],
455+
"start": Object {
456+
"column": 1,
457+
"line": 1,
458+
"offset": 0,
459+
},
460+
},
461+
"type": "heading",
462+
}
463+
`;
464+
320465
exports[`gatsby-remark-autolink-headers maintain case of markdown header for id 1`] = `
321466
Object {
322467
"children": Array [

packages/gatsby-remark-autolink-headers/src/__tests__/index.js

+98
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ describe(`gatsby-remark-autolink-headers`, () => {
239239
},
240240
])
241241
})
242+
242243
it(`adds places anchor after header when isIconAfterHeader prop is passed`, () => {
243244
const markdownAST = remark.parse(`# Heading Uno`)
244245

@@ -253,4 +254,101 @@ describe(`gatsby-remark-autolink-headers`, () => {
253254
expect(node).toMatchSnapshot()
254255
})
255256
})
257+
258+
it(`adds id to a markdown heading when elements prop is passed with matching heading type`, () => {
259+
const markdownAST = remark.parse(`# Heading Uno`)
260+
261+
const transformed = plugin({ markdownAST }, { elements: [`h1`] })
262+
263+
visit(transformed, `heading`, node => {
264+
expect(node.data.id).toBeDefined()
265+
266+
expect(node).toMatchSnapshot()
267+
})
268+
})
269+
270+
it(`does not add data to a markdown heading when elements prop is passed with no matching heading type`, () => {
271+
const markdownAST = remark.parse(`# Heading Uno`)
272+
273+
const transformed = plugin({ markdownAST }, { elements: [`h2`] })
274+
275+
visit(transformed, `heading`, node => {
276+
expect(node.data).not.toBeDefined()
277+
278+
expect(node).toMatchSnapshot()
279+
})
280+
})
281+
282+
it(`does not add data to a markdown heading with custom id when elements prop is passed with no matching heading type`, () => {
283+
const markdownAST = remark.parse(`# Heading Uno {#custom_h1}`)
284+
285+
const transformed = plugin({ markdownAST }, { elements: [`h2`] })
286+
287+
visit(transformed, `heading`, node => {
288+
expect(node.data).not.toBeDefined()
289+
290+
expect(node).toMatchSnapshot()
291+
})
292+
})
293+
294+
it(`only adds ids to markdown headings whose heading type is included in the passed elements prop`, () => {
295+
const markdownAST = remark.parse(`
296+
# Heading One
297+
298+
## Heading Two
299+
300+
### Heading Three
301+
`)
302+
303+
const transformed = plugin({ markdownAST }, { elements: [`h2`] })
304+
305+
visit(transformed, `heading`, node => {
306+
if (node.depth === 2) {
307+
expect(node.data.id).toBeDefined()
308+
} else {
309+
expect(node.data).not.toBeDefined()
310+
}
311+
})
312+
})
313+
314+
it(`does not add data to markdown headings when an empty array elements prop is passed`, () => {
315+
const markdownAST = remark.parse(`
316+
# Heading One
317+
318+
## Heading Two
319+
320+
### Heading Three
321+
`)
322+
323+
const transformed = plugin({ markdownAST }, { elements: [] })
324+
325+
visit(transformed, `heading`, node => {
326+
expect(node.data).not.toBeDefined()
327+
})
328+
})
329+
330+
it(`allows all six heading depths to be passed in the elements prop`, () => {
331+
const markdownAST = remark.parse(`
332+
# Heading One
333+
334+
## Heading Two
335+
336+
### Heading Three
337+
338+
#### Heading Four
339+
340+
##### Heading Five
341+
342+
###### Heading Six
343+
`)
344+
345+
const transformed = plugin(
346+
{ markdownAST },
347+
{ elements: [`h1`, `h2`, `h3`, `h4`, `h5`, `h6`] }
348+
)
349+
350+
visit(transformed, `heading`, node => {
351+
expect(node.data.id).toBeDefined()
352+
})
353+
})
256354
})

packages/gatsby-remark-autolink-headers/src/index.js

+6
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,17 @@ module.exports = (
2222
removeAccents = false,
2323
enableCustomId = false,
2424
isIconAfterHeader = false,
25+
elements = null,
2526
}
2627
) => {
2728
slugs.reset()
2829

2930
visit(markdownAST, `heading`, node => {
31+
// If elements array exists, do not create links for heading types not included in array
32+
if (Array.isArray(elements) && !elements.includes(`h${node.depth}`)) {
33+
return
34+
}
35+
3036
let id
3137
if (enableCustomId && node.children.length > 0) {
3238
const last = node.children[node.children.length - 1]

0 commit comments

Comments
 (0)