Skip to content

Commit 8e33b72

Browse files
authored
MD component prop pass-thru (#996)
* MD component pass-thru * Add docs/tests * Update index.d.ts * Update api-reference.md * Update example to include componentProps
1 parent 4e9a5ad commit 8e33b72

File tree

6 files changed

+133
-13
lines changed

6 files changed

+133
-13
lines changed

docs/content/api-reference.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -174,10 +174,11 @@ Image is a component to display a picture within a slide. It is analogous to an
174174

175175
The Markdown components let you include a block of Markdown within a slide using `<Markdown />`, author a complete slide with Markdown using `<MarkdownSlide />`, or author a series of slides with Markdown using `<MarkdownSlides />`. Markdown tags get converted into Spectacle components. The `---` three dash marker when used inside `<MarkdownSlideSet />` is used to divide content into separate slides. Markdown also supports presenter notes using the `Notes:` marker. `<Markdown />` must be a child of `<Slide />` where `<MarkdownSlide />` and `<MarkdownSlideSet />` are children of `<Deck />`.
176176

177-
| Props | Type | Example |
178-
| ------------------ | ----------------- | ------------------------------------ |
179-
| `children` | PropTypes.string | `# Hi there` |
180-
| `animateListItems` | PropTypes.boolean | `<MarkdownSlide animateListItems />` |
177+
| Props | Type | Example |
178+
| ------------------ | ----------------- | ----------------------------------------------------------------------------------- |
179+
| `children` | PropTypes.string | `# Hi there` |
180+
| `componentProps` | PropTypes.object | `<MarkdownSlide componentProps={{ color: 'purple' }}># I'm purple!</MarkdownSlide>` |
181+
| `animateListItems` | PropTypes.boolean | `<MarkdownSlide animateListItems />` |
181182

182183
```jsx
183184
<Slide>

examples/js/index.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,9 +192,12 @@ const Presentation = () => (
192192
<Heading>This is a slide embedded in a div</Heading>
193193
</Slide>
194194
</div>
195-
<MarkdownSlide>
195+
<MarkdownSlide componentProps={{ color: 'yellow' }}>
196196
{`
197197
# This is a Markdown Slide
198+
199+
- You can pass props down to all elements on the slide.
200+
- Just use the \`componentProps\` prop.
198201
`}
199202
</MarkdownSlide>
200203
<MarkdownSlide animateListItems>

examples/one-page.html

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,9 +179,25 @@
179179
<${Heading}>This is a slide embedded in a div</${Heading}>
180180
</${Slide}>
181181
</div>
182-
<${MarkdownSlide}>
182+
<${MarkdownSlide} componentProps=${{
183+
color: 'yellow'
184+
}}>
183185
${`
184186
# This is a Markdown Slide
187+
188+
- You can pass props down to all elements on the slide.
189+
- Just use the \`componentProps\` prop.
190+
`}
191+
</${MarkdownSlide}>
192+
<${MarkdownSlide} animateListItems>
193+
${`
194+
# This is also a Markdown Slide
195+
196+
It uses the \`animateListItems\` prop.
197+
198+
- Its list items...
199+
- they will appear in...
200+
- one at a time.
185201
`}
186202
</${MarkdownSlide}>
187203
<${MarkdownSlideSet}>

index.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,19 +105,24 @@ declare module 'spectacle' {
105105
size: number;
106106
}>;
107107

108+
type MdComponentProps = { [key: string]: any };
109+
108110
export const Markdown: React.FC<{
109111
animateListItems?: boolean;
110112
children: React.ReactNode;
113+
componentProps?: MdComponentProps;
111114
}>;
112115

113116
export const MarkdownSlide: React.FC<{
114117
animateListItems?: boolean;
115118
children: React.ReactNode;
119+
componentProps?: MdComponentProps;
116120
}>;
117121

118122
export const MarkdownSlideSet: React.FC<{
119123
animateListItems?: boolean;
120124
children: React.ReactNode;
125+
componentProps?: MdComponentProps;
121126
}>;
122127

123128
export const SpectacleLogo: React.FC<{

src/components/markdown/markdown.js

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ export const Markdown = ({
2727
getPropsForAST: () => {}
2828
},
2929
children: rawMarkdownText,
30-
animateListItems = false
30+
animateListItems = false,
31+
componentProps = {}
3132
}) => {
3233
const {
3334
theme: { markdownComponentMap: themeComponentMap = {} } = {}
@@ -85,12 +86,19 @@ export const Markdown = ({
8586
CodeBlockComponent
8687
);
8788

89+
const componentMapWithPassedThroughProps = Object.entries(
90+
componentMap
91+
).reduce((newMap, [key, Component]) => {
92+
newMap[key] = props => <Component {...props} {...componentProps} />;
93+
return newMap;
94+
}, {});
95+
8896
// Create the compiler for the _user-visible_ markdown (not presenter notes)
8997
const compiler = unified()
9098
.use(remark2rehype)
9199
.use(rehype2react, {
92100
createElement: React.createElement,
93-
components: componentMap
101+
components: componentMapWithPassedThroughProps
94102
});
95103

96104
// Compile each of the values we got back from the template function
@@ -126,9 +134,10 @@ export const Markdown = ({
126134
}, [
127135
rawMarkdownText,
128136
getPropsForAST,
129-
userProvidedComponentMap,
130137
themeComponentMap,
131-
animateListItems
138+
userProvidedComponentMap,
139+
animateListItems,
140+
componentProps
132141
]);
133142

134143
const { children, ...restProps } = templateProps;
@@ -153,11 +162,20 @@ export const MarkdownSlide = ({
153162
componentMap,
154163
template,
155164
animateListItems = false,
165+
componentProps = {},
156166
...rest
157167
}) => {
158168
return (
159169
<Slide {...rest}>
160-
<Markdown {...{ componentMap, template, animateListItems, children }} />
170+
<Markdown
171+
{...{
172+
componentMap,
173+
template,
174+
animateListItems,
175+
componentProps,
176+
children
177+
}}
178+
/>
161179
</Slide>
162180
);
163181
};

src/components/markdown/markdown.test.js

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import React from 'react';
22
import Enzyme, { mount } from 'enzyme';
3-
import { MarkdownSlide, MarkdownSlideSet } from './markdown';
3+
import { Markdown, MarkdownSlide, MarkdownSlideSet } from './markdown';
44
import Adapter from 'enzyme-adapter-react-16';
55
import Deck from '../deck/deck';
6-
import { ListItem } from '../typography';
6+
import { Heading, ListItem } from '../typography';
77
import Appear from '../appear';
8+
import Slide from '../slide/slide';
89

910
Enzyme.configure({ adapter: new Adapter() });
1011

@@ -80,4 +81,80 @@ describe('<MarkdownSlideSet />', () => {
8081
expect(wrapper.find('ul')).toHaveLength(2);
8182
expect(wrapper.find(Appear)).toHaveLength(6);
8283
});
84+
85+
it('Markdown should pass componentProps down to constituent components', () => {
86+
const wrapper = mountInsideDeck(
87+
<Slide>
88+
<Heading>Im not styled...</Heading>
89+
<Markdown componentProps={{ color: 'purple' }}>{`
90+
# What's up world, I'm styled.
91+
92+
- List item
93+
- And another one
94+
`}</Markdown>
95+
</Slide>
96+
);
97+
98+
expect(
99+
wrapper
100+
.find(Heading)
101+
.at(0)
102+
.prop('color')
103+
).not.toBe('purple');
104+
105+
expect(
106+
wrapper
107+
.find(Heading)
108+
.at(1)
109+
.prop('color')
110+
).toBe('purple');
111+
112+
expect(
113+
wrapper
114+
.find(ListItem)
115+
.at(0)
116+
.prop('color')
117+
).toBe('purple');
118+
});
119+
120+
it('MarkdownSlide should pass componentProps down to constituent components', () => {
121+
const wrapper = mountInsideDeck(
122+
<MarkdownSlide componentProps={{ color: 'purple' }}>{`
123+
# What's up world, I'm styled.
124+
`}</MarkdownSlide>
125+
);
126+
127+
expect(
128+
wrapper
129+
.find(Heading)
130+
.at(0)
131+
.prop('color')
132+
).toBe('purple');
133+
});
134+
135+
it('MarkdownSlideSet should pass componentProps down to constituent components', () => {
136+
const wrapper = mountInsideDeck(
137+
<MarkdownSlideSet componentProps={{ color: 'purple' }}>{`
138+
# What's up world, I'm styled.
139+
140+
---
141+
142+
# Another slide
143+
`}</MarkdownSlideSet>
144+
);
145+
146+
expect(
147+
wrapper
148+
.find(Heading)
149+
.at(0)
150+
.prop('color')
151+
).toBe('purple');
152+
153+
expect(
154+
wrapper
155+
.find(Heading)
156+
.at(1)
157+
.prop('color')
158+
).toBe('purple');
159+
});
83160
});

0 commit comments

Comments
 (0)