Merge pull request #13212 from johnphilip283/markdown_ids
Autogenerate ids for headings in markdown content
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/core-components': minor
|
||||
---
|
||||
|
||||
Adds code to generate ids for headers parsed through the MarkdownContent component.
|
||||
@@ -100,4 +100,34 @@ describe('<MarkdownContent />', () => {
|
||||
'https://example.com/blog/assets/6/header.png',
|
||||
);
|
||||
});
|
||||
|
||||
it('render MarkdownContent component with headings given proper ids', async () => {
|
||||
const rendered = await renderWithEffects(
|
||||
wrapInTestApp(
|
||||
<MarkdownContent
|
||||
content={
|
||||
'# Lorem ipsum\n' +
|
||||
'## bing bong\n' +
|
||||
'### The FitnessGram Pacer Test is a multistage aerobic capacity test'
|
||||
}
|
||||
/>,
|
||||
),
|
||||
);
|
||||
|
||||
expect(rendered.getByText('Lorem ipsum').getAttribute('id')).toEqual(
|
||||
'lorem-ipsum',
|
||||
);
|
||||
expect(rendered.getByText('bing bong').getAttribute('id')).toEqual(
|
||||
'bing-bong',
|
||||
);
|
||||
expect(
|
||||
rendered
|
||||
.getByText(
|
||||
'The FitnessGram Pacer Test is a multistage aerobic capacity test',
|
||||
)
|
||||
.getAttribute('id'),
|
||||
).toEqual(
|
||||
'the-fitnessgram-pacer-test-is-a-multistage-aerobic-capacity-test',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,6 +20,7 @@ import gfm from 'remark-gfm';
|
||||
import React from 'react';
|
||||
import { BackstageTheme } from '@backstage/theme';
|
||||
import { CodeSnippet } from '../CodeSnippet';
|
||||
import { HeadingProps } from 'react-markdown/lib/ast-to-react';
|
||||
|
||||
export type MarkdownContentClassKey = 'markdown';
|
||||
|
||||
@@ -73,6 +74,21 @@ type Props = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const flatten = (text: string, child: any): string => {
|
||||
if (!child) return text;
|
||||
|
||||
return typeof child === 'string'
|
||||
? text + child
|
||||
: React.Children.toArray(child.props.children).reduce(flatten, text);
|
||||
};
|
||||
|
||||
const headingRenderer = ({ level, children }: HeadingProps) => {
|
||||
const childrenArray = React.Children.toArray(children);
|
||||
const text = childrenArray.reduce(flatten, '');
|
||||
const slug = text.toLocaleLowerCase('en-US').replace(/\W/g, '-');
|
||||
return React.createElement(`h${level}`, { id: slug }, children);
|
||||
};
|
||||
|
||||
const components: Options['components'] = {
|
||||
code: ({ inline, className, children, ...props }) => {
|
||||
const text = String(children).replace(/\n+$/, '');
|
||||
@@ -85,6 +101,12 @@ const components: Options['components'] = {
|
||||
</code>
|
||||
);
|
||||
},
|
||||
h1: headingRenderer,
|
||||
h2: headingRenderer,
|
||||
h3: headingRenderer,
|
||||
h4: headingRenderer,
|
||||
h5: headingRenderer,
|
||||
h6: headingRenderer,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user