diff --git a/docs/content/api-reference.md b/docs/content/api-reference.md
index 3288d8e51..44141e044 100644
--- a/docs/content/api-reference.md
+++ b/docs/content/api-reference.md
@@ -174,10 +174,11 @@ Image is a component to display a picture within a slide. It is analogous to an
The Markdown components let you include a block of Markdown within a slide using ``, author a complete slide with Markdown using ``, or author a series of slides with Markdown using ``. Markdown tags get converted into Spectacle components. The `---` three dash marker when used inside `` is used to divide content into separate slides. Markdown also supports presenter notes using the `Notes:` marker. `` must be a child of `` where `` and `` are children of ``.
-| Props | Type | Example |
-| ------------------ | ----------------- | ------------------------------------ |
-| `children` | PropTypes.string | `# Hi there` |
-| `animateListItems` | PropTypes.boolean | `` |
+| Props | Type | Example |
+| ------------------ | ----------------- | ----------------------------------------------------------------------------------- |
+| `children` | PropTypes.string | `# Hi there` |
+| `componentProps` | PropTypes.object | `# I'm purple!` |
+| `animateListItems` | PropTypes.boolean | `` |
```jsx
diff --git a/examples/js/index.js b/examples/js/index.js
index 412ef8e2d..d18867f4f 100644
--- a/examples/js/index.js
+++ b/examples/js/index.js
@@ -192,9 +192,12 @@ const Presentation = () => (
This is a slide embedded in a div
-
+
{`
# This is a Markdown Slide
+
+ - You can pass props down to all elements on the slide.
+ - Just use the \`componentProps\` prop.
`}
diff --git a/examples/one-page.html b/examples/one-page.html
index 47eb017b6..286abd1ae 100644
--- a/examples/one-page.html
+++ b/examples/one-page.html
@@ -179,9 +179,25 @@
<${Heading}>This is a slide embedded in a div${Heading}>
${Slide}>
- <${MarkdownSlide}>
+ <${MarkdownSlide} componentProps=${{
+ color: 'yellow'
+ }}>
${`
# This is a Markdown Slide
+
+ - You can pass props down to all elements on the slide.
+ - Just use the \`componentProps\` prop.
+ `}
+ ${MarkdownSlide}>
+ <${MarkdownSlide} animateListItems>
+ ${`
+ # This is also a Markdown Slide
+
+ It uses the \`animateListItems\` prop.
+
+ - Its list items...
+ - they will appear in...
+ - one at a time.
`}
${MarkdownSlide}>
<${MarkdownSlideSet}>
diff --git a/index.d.ts b/index.d.ts
index b68b37dd1..45ac800b0 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -105,19 +105,24 @@ declare module 'spectacle' {
size: number;
}>;
+ type MdComponentProps = { [key: string]: any };
+
export const Markdown: React.FC<{
animateListItems?: boolean;
children: React.ReactNode;
+ componentProps?: MdComponentProps;
}>;
export const MarkdownSlide: React.FC<{
animateListItems?: boolean;
children: React.ReactNode;
+ componentProps?: MdComponentProps;
}>;
export const MarkdownSlideSet: React.FC<{
animateListItems?: boolean;
children: React.ReactNode;
+ componentProps?: MdComponentProps;
}>;
export const SpectacleLogo: React.FC<{
diff --git a/src/components/markdown/markdown.js b/src/components/markdown/markdown.js
index 50f4fb914..a41625e12 100644
--- a/src/components/markdown/markdown.js
+++ b/src/components/markdown/markdown.js
@@ -27,7 +27,8 @@ export const Markdown = ({
getPropsForAST: () => {}
},
children: rawMarkdownText,
- animateListItems = false
+ animateListItems = false,
+ componentProps = {}
}) => {
const {
theme: { markdownComponentMap: themeComponentMap = {} } = {}
@@ -85,12 +86,19 @@ export const Markdown = ({
CodeBlockComponent
);
+ const componentMapWithPassedThroughProps = Object.entries(
+ componentMap
+ ).reduce((newMap, [key, Component]) => {
+ newMap[key] = props => ;
+ return newMap;
+ }, {});
+
// Create the compiler for the _user-visible_ markdown (not presenter notes)
const compiler = unified()
.use(remark2rehype)
.use(rehype2react, {
createElement: React.createElement,
- components: componentMap
+ components: componentMapWithPassedThroughProps
});
// Compile each of the values we got back from the template function
@@ -126,9 +134,10 @@ export const Markdown = ({
}, [
rawMarkdownText,
getPropsForAST,
- userProvidedComponentMap,
themeComponentMap,
- animateListItems
+ userProvidedComponentMap,
+ animateListItems,
+ componentProps
]);
const { children, ...restProps } = templateProps;
@@ -153,11 +162,20 @@ export const MarkdownSlide = ({
componentMap,
template,
animateListItems = false,
+ componentProps = {},
...rest
}) => {
return (
-
+
);
};
diff --git a/src/components/markdown/markdown.test.js b/src/components/markdown/markdown.test.js
index 4d8d044b5..cc59088c2 100644
--- a/src/components/markdown/markdown.test.js
+++ b/src/components/markdown/markdown.test.js
@@ -1,10 +1,11 @@
import React from 'react';
import Enzyme, { mount } from 'enzyme';
-import { MarkdownSlide, MarkdownSlideSet } from './markdown';
+import { Markdown, MarkdownSlide, MarkdownSlideSet } from './markdown';
import Adapter from 'enzyme-adapter-react-16';
import Deck from '../deck/deck';
-import { ListItem } from '../typography';
+import { Heading, ListItem } from '../typography';
import Appear from '../appear';
+import Slide from '../slide/slide';
Enzyme.configure({ adapter: new Adapter() });
@@ -80,4 +81,80 @@ describe('', () => {
expect(wrapper.find('ul')).toHaveLength(2);
expect(wrapper.find(Appear)).toHaveLength(6);
});
+
+ it('Markdown should pass componentProps down to constituent components', () => {
+ const wrapper = mountInsideDeck(
+
+ Im not styled...
+ {`
+ # What's up world, I'm styled.
+
+ - List item
+ - And another one
+ `}
+
+ );
+
+ expect(
+ wrapper
+ .find(Heading)
+ .at(0)
+ .prop('color')
+ ).not.toBe('purple');
+
+ expect(
+ wrapper
+ .find(Heading)
+ .at(1)
+ .prop('color')
+ ).toBe('purple');
+
+ expect(
+ wrapper
+ .find(ListItem)
+ .at(0)
+ .prop('color')
+ ).toBe('purple');
+ });
+
+ it('MarkdownSlide should pass componentProps down to constituent components', () => {
+ const wrapper = mountInsideDeck(
+ {`
+ # What's up world, I'm styled.
+ `}
+ );
+
+ expect(
+ wrapper
+ .find(Heading)
+ .at(0)
+ .prop('color')
+ ).toBe('purple');
+ });
+
+ it('MarkdownSlideSet should pass componentProps down to constituent components', () => {
+ const wrapper = mountInsideDeck(
+ {`
+ # What's up world, I'm styled.
+
+ ---
+
+ # Another slide
+ `}
+ );
+
+ expect(
+ wrapper
+ .find(Heading)
+ .at(0)
+ .prop('color')
+ ).toBe('purple');
+
+ expect(
+ wrapper
+ .find(Heading)
+ .at(1)
+ .prop('color')
+ ).toBe('purple');
+ });
});