Skip to content

Commit cdc0afe

Browse files
feat: Add code pane layouts (#1217)
* Add code pane layout options. * Add documentation for code layouts. * Add tests and update api somewhat. * Remove comments, undo irrelevant change. * Add changeset. * Update naming in api docs. * Rename Code layout. * Replace center justified content with scrollable content. Allow for codepane grid. * Pass CodePaneProps in a more typescript and friendly manner. * Update docs and tests. * Remove unused code. * Update slide-layout types * Update api-reference.md * RM showInlineLineNumbers Co-authored-by: Grant Sander <[email protected]>
1 parent fb9d374 commit cdc0afe

File tree

5 files changed

+201
-5
lines changed

5 files changed

+201
-5
lines changed

.changeset/shiny-dingos-check.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'spectacle': minor
3+
---
4+
5+
feat: Add single and multiple code pane Slide Layouts with options.

docs/api-reference.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,3 +448,38 @@ A vertically-centered Quote layout for if you want to present a quote and attrib
448448
| `attribution` | `ReactNode` | ✅ | `William Shakespeare` |
449449
| `quoteProps` | [Text Props](#typography-tags) | ❌ | { fontSize: "100px" } |
450450
| `attributionProps` | [Text Props](#typography-tags) | ❌ | { fontSize: "48px" } |
451+
452+
### `SlideLayout.Code`
453+
454+
A layout with a single code pane and an optional title for if you want one code block per slide.
455+
456+
| Props | Type | Required | Example |
457+
|-----------------|-----------------------------------|----------|------------------------------------------------------------------|
458+
| `...slideProps` | [Slide Props](#slide) | ❌ | |
459+
| `title` | `string` | ❌ | `Show me the code!` |
460+
| `titleProps` | [Heading Props](#typography-tags) | ❌ | `{ color: 'red' }` |
461+
| `children` | `string` | ✅ | `const Component = (props: componentProps): JSX.Element = {...}` |
462+
| `language` | `boolean` | ✅ | `false` |
463+
| `codePaneProps` | `CodePaneProps` | ❌ | |
464+
465+
### `SlideLayout.MultiCodeLayout`
466+
467+
A layout with multiple code panes and optional descriptions, and an optional title for if you want more than one code block per slide or code with description text.
468+
469+
| Props | Type | Required | Example |
470+
|-----------------|-----------------------------------|----------|---------------------------------------------------------------------------------------------------------------------|
471+
| `...slideProps` | [Slide Props](#slide) | ❌ | |
472+
| `title` | `string` | ❌ | `Show me the code!` |
473+
| `titleProps` | [Heading Props](#typography-tags) | ❌ | `{ color: 'red' }` |
474+
| `numColumns` | `number` | ❌ | `{2}` |
475+
| `codeBlocks` | `CodeBlock[]` | ✅ | `[{ code: 'console.log("hello world!")', language: 'jsx', description: 'Say hello', codePaneProps: {...} }, {...}]` |
476+
477+
where
478+
479+
```ts
480+
type CodeBlock = Omit<CodePaneProps, 'children'> & {
481+
code: CodePaneProps['children'];
482+
description?: string | ReactNode;
483+
descriptionProps?: ComponentProps<typeof Text>;
484+
}
485+
```

packages/spectacle/src/components/code-pane.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,15 +149,13 @@ const CodePane = forwardRef<HTMLDivElement, CodePaneProps>(
149149
* default theme with no valid values.
150150
*/
151151
const {
152-
size: { width = 1366 },
153152
space = [0, 0, 0],
154153
fontSizes: { monospace = '20px' }
155154
} = theme;
156155

157156
return {
158157
padding: space[0],
159158
margin: 0,
160-
width: width - space[2] * 2 - space[0] * 2,
161159
fontSize: monospace
162160
};
163161
}, [theme]);

packages/spectacle/src/components/slide-layout.test.tsx

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,4 +272,59 @@ describe('SlideLayout', () => {
272272
fontSize: '48px'
273273
});
274274
});
275+
276+
it('SlideLayout.Code should render a titled slide with title props passed through', () => {
277+
const { getByText } = renderInDeck(
278+
<SlideLayout.Code
279+
language={'js'}
280+
title={'Hello World!'}
281+
titleProps={{ fontSize: '24px' }}
282+
>
283+
{'console.log("Hello World!");'}
284+
</SlideLayout.Code>
285+
);
286+
287+
expect(getByText('Hello World!')).toHaveStyle({ fontSize: '24px' });
288+
});
289+
290+
it('SlideLayout.MultiCodeLayout should contain more than one code pane', () => {
291+
const { queryAllByTestId } = renderInDeck(
292+
<SlideLayout.MultiCodeLayout
293+
codeBlocks={[
294+
{ code: `const greeting = 'hello world.'`, language: `jsx` },
295+
{ code: `const greeting = 'hello again world.'`, language: `jsx` }
296+
]}
297+
/>
298+
);
299+
300+
expect(queryAllByTestId('CodePane')).toHaveLength(2);
301+
});
302+
303+
it('SlideLayout.MultiCodeLayout should render multiple code panes with description props passed through', () => {
304+
const { getByText } = renderInDeck(
305+
<SlideLayout.MultiCodeLayout
306+
codeBlocks={[
307+
{
308+
code: `let greeting = 'hello world.'`,
309+
language: `jsx`,
310+
description: `assign a variable to a string.`,
311+
descriptionProps: { color: 'blue' }
312+
},
313+
{
314+
code: `greeting = 'hello again world.'`,
315+
language: `jsx`,
316+
description: `reassign the variable.`,
317+
descriptionProps: { color: 'cyan' }
318+
}
319+
]}
320+
/>
321+
);
322+
323+
expect(getByText('assign a variable to a string.')).toHaveStyle({
324+
color: 'blue'
325+
});
326+
expect(getByText('reassign the variable.')).toHaveStyle({
327+
color: 'cyan'
328+
});
329+
});
275330
});

packages/spectacle/src/components/slide-layout.tsx

Lines changed: 106 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import * as React from 'react';
12
import Slide, { SlideProps } from './slide/slide';
2-
import { Box, FlexBox } from './layout-primitives';
3+
import { Box, FlexBox, Grid } from './layout-primitives';
4+
import CodePane, { CodePaneProps } from './code-pane';
35
import { ComponentProps, Fragment, ReactNode } from 'react';
46
import {
57
Heading,
@@ -192,11 +194,110 @@ const Quote = ({
192194
</Slide>
193195
);
194196

197+
/**
198+
* Generic Codepane utility with optional Description text
199+
*/
200+
const CodeLayout = ({
201+
text,
202+
textProps,
203+
children,
204+
...props
205+
}: CodePaneProps & {
206+
text?: string | ReactNode;
207+
textProps?: ComponentProps<typeof Text>;
208+
}) => (
209+
<Box data-testid="CodePane">
210+
{text ? (
211+
<Text margin={8} {...textProps}>
212+
{text}
213+
</Text>
214+
) : null}
215+
<CodePane {...props}>{children}</CodePane>
216+
</Box>
217+
);
218+
219+
/**
220+
* single Code Pane with optional Title layout
221+
*/
222+
const Code = ({
223+
children,
224+
language,
225+
title,
226+
titleProps,
227+
codePaneProps,
228+
...rest
229+
}: Omit<SlideProps, 'children'> & {
230+
children: string;
231+
language: string;
232+
title?: string | ReactNode;
233+
titleProps?: ComponentProps<typeof Text>;
234+
codePaneProps?: CodePaneProps;
235+
}) => {
236+
return (
237+
<Slide {...rest}>
238+
<Box display="inline-block" style={{ overflow: 'scroll' }}>
239+
{title ? <Heading {...titleProps}>{title}</Heading> : null}
240+
<CodeLayout language={language} {...codePaneProps}>
241+
{children}
242+
</CodeLayout>
243+
</Box>
244+
</Slide>
245+
);
246+
};
247+
248+
/**
249+
* multiple Code Panes with optional Description, with optional Title layout
250+
*/
251+
const MultiCodeLayout = ({
252+
codeBlocks,
253+
title,
254+
titleProps,
255+
numColumns = 1,
256+
...rest
257+
}: Omit<SlideProps, 'children'> & {
258+
codeBlocks: Array<
259+
Omit<CodePaneProps, 'children'> & {
260+
code: CodePaneProps['children'];
261+
description?: string | ReactNode;
262+
descriptionProps?: ComponentProps<typeof Text>;
263+
}
264+
>;
265+
title?: string | ReactNode;
266+
titleProps?: ComponentProps<typeof Text>;
267+
numColumns?: number;
268+
}) => {
269+
return (
270+
<Slide {...rest}>
271+
<Box display="inline-block" style={{ overflow: 'scroll' }}>
272+
{title ? <Heading {...titleProps}>{title}</Heading> : null}
273+
<Grid
274+
gridRowGap={1}
275+
gridColumnGap={1}
276+
gridTemplateColumns={`repeat(${numColumns}, minmax(100px, 1fr))`}
277+
maxWidth="100%"
278+
>
279+
{codeBlocks.map(
280+
({ description, descriptionProps, code, ...codePaneProps }, i) => (
281+
<CodeLayout
282+
key={i}
283+
text={description}
284+
textProps={descriptionProps}
285+
{...codePaneProps}
286+
>
287+
{code}
288+
</CodeLayout>
289+
)
290+
)}
291+
</Grid>
292+
</Box>
293+
</Slide>
294+
);
295+
};
296+
195297
/**
196298
* Layouts to consider:
197299
* - Image (left, right, full bleed?)
198300
* - Intro
199-
* - Code Snippet (syntax highlighting)
200301
*/
201302

202303
export default {
@@ -207,5 +308,7 @@ export default {
207308
Section,
208309
BigFact,
209310
Quote,
210-
Statement
311+
Statement,
312+
Code,
313+
MultiCodeLayout
211314
};

0 commit comments

Comments
 (0)