Skip to content

Commit ee95207

Browse files
feat: Add new slide layouts: section, statement, big fact, quote. (#1209)
* Add slide layout options: Section, Statement, Big fact, Quote. * Add tests for new slide layouts. * Add docs for new slide layouts. * Run prettier. * Use generic header component for Section and Statement layouts. * Add big fact font size prop. * Use dash character in quote attribution. * Update types of accepted heading content. * Update tests. * Update api reference docs. * Update changelog. * Allow children in Section and Statement layouts. * Update packages/spectacle/src/components/slide-layout.tsx Co-authored-by: Grant Sander <gksander93@gmail.com> * Update docs/api-reference.md Co-authored-by: Grant Sander <gksander93@gmail.com> * Update docs/api-reference.md Co-authored-by: Grant Sander <gksander93@gmail.com> * Update packages/spectacle/src/components/slide-layout.tsx Co-authored-by: Grant Sander <gksander93@gmail.com> Co-authored-by: Grant Sander <gksander93@gmail.com>
1 parent f470390 commit ee95207

File tree

4 files changed

+318
-6
lines changed

4 files changed

+318
-6
lines changed

.changeset/little-peaches-jump.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 new Slide Layouts: Section, Statement, Big fact, Quote to expand basic layout creation.

docs/api-reference.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,3 +404,47 @@ A layout with a list and an optional title for if you want to quickly display a
404404
| `items` | `ReactNode[]` | ✅ | `['Hello', <Text>World</Text>]` |
405405
| `animateListItems` | `boolean` | ❌ | `true` |
406406
| `listProps` | [List Props](#typography-tags) | ❌ | `{ backgroundColor: 'purple' }` |
407+
408+
409+
### `SlideLayout.Section`
410+
411+
A vertically-centered left-aligned section title layout for if you want title page for a new section.
412+
413+
| Props | Type | Required | Example |
414+
|------------------------|---------------------------------|----------|--------------------------|
415+
| `...slideProps` | [Slide Props](#slide) | ❌ | |
416+
| `sectionProps` | [Text Props](#typography-tags) | ❌ | { fontSize: "48px" } |
417+
418+
### `SlideLayout.Statement`
419+
420+
A vertically-centered center-aligned statement for if you want to make a statement.
421+
422+
| Props | Type | Required | Example |
423+
|------------------------|---------------------------------|----------|--------------------------|
424+
| `...slideProps` | [Slide Props](#slide) | ❌ | |
425+
| `statementProps` | [Text Props](#typography-tags) | ❌ | { fontSize: "48px" } |
426+
427+
### `SlideLayout.BigFact`
428+
429+
A centered Big Fact layout for if you want to present a fact in a large font.
430+
431+
| Props | Type | Required | Example | Default |
432+
|---------------------------|---------------------------------|----------|------------------------|---------|
433+
| `...slideProps` | [Slide Props](#slide) | ❌ | | |
434+
| `fact` | `string | ReactNode` | ✅ | `100%` | |
435+
| `factInformation` | `string | ReactNode` | ❌ | `Fact information` | |
436+
| `factProps` | [Text Props](#typography-tags) | ❌ | { fontSize: "100px" } | |
437+
| `factInformationProps` | [Text Props](#typography-tags) | ❌ | { fontSize: "48px" } | |
438+
| `factFontSize` | `string` | ❌ | `150px` |`250px` |
439+
440+
### `SlideLayout.Quote`
441+
442+
A vertically-centered Quote layout for if you want to present a quote and attribute it to someone.
443+
444+
| Props | Type | Required | Example |
445+
|-----------------------|---------------------------------|----------|------------------------|
446+
| `...slideProps` | [Slide Props](#slide) | ❌ | |
447+
| `quote` | `string | ReactNode` | ✅ | `To be, or not to be` |
448+
| `attribution` | `string | ReactNode` | ✅ | `William Shakespeare` |
449+
| `quoteProps` | [Text Props](#typography-tags) | ❌ | { fontSize: "100px" } |
450+
| `attributionProps` | [Text Props](#typography-tags) | ❌ | { fontSize: "48px" } |

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

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,4 +120,148 @@ describe('SlideLayout', () => {
120120

121121
expect(queryAllByTestId('AppearElement')).toHaveLength(3);
122122
});
123+
124+
it('SlideLayout.Section should render a section title', () => {
125+
const { getByText } = renderInDeck(
126+
<SlideLayout.Section>{'Section title'}</SlideLayout.Section>
127+
);
128+
129+
expect(getByText('Section title')).toBeDefined();
130+
});
131+
132+
it('SlideLayout.Section should render a section title within a react node', () => {
133+
const { getByText } = renderInDeck(
134+
<SlideLayout.Section>
135+
{
136+
<>
137+
Hello<em>World!</em>
138+
</>
139+
}
140+
</SlideLayout.Section>
141+
);
142+
143+
expect(getByText('World!')).toBeDefined();
144+
});
145+
146+
it('SlideLayout.Section should render a section slide with props passed through', () => {
147+
const { getByText } = renderInDeck(
148+
<SlideLayout.Section sectionProps={{ fontSize: '68px' }}>
149+
{'Section title'}
150+
</SlideLayout.Section>
151+
);
152+
153+
expect(getByText('Section title')).toHaveStyle({ fontSize: '68px' });
154+
});
155+
156+
it('SlideLayout.Section should render a section title in a left aligned flexbox', () => {
157+
const { getByText } = renderInDeck(
158+
<SlideLayout.Section>{'Section title'}</SlideLayout.Section>
159+
);
160+
161+
expect(getByText('Section title').parentElement).toHaveStyle({
162+
justifyContent: 'flex-start'
163+
});
164+
});
165+
166+
it('SlideLayout.Statement should render statement text', () => {
167+
const { getByText } = renderInDeck(
168+
<SlideLayout.Statement>{'Statement'}</SlideLayout.Statement>
169+
);
170+
171+
expect(getByText('Statement')).toBeDefined();
172+
});
173+
174+
it('SlideLayout.Statement should render statement text within a react node', () => {
175+
const { getByText } = renderInDeck(
176+
<SlideLayout.Statement>
177+
{
178+
<>
179+
Hello<em>World!</em>
180+
</>
181+
}
182+
</SlideLayout.Statement>
183+
);
184+
185+
expect(getByText('World!')).toBeDefined();
186+
});
187+
188+
it('SlideLayout.Statement should render a statement slide with props passed through', () => {
189+
const { getByText } = renderInDeck(
190+
<SlideLayout.Statement statementProps={{ fontSize: '88px' }}>
191+
{'Statement'}
192+
</SlideLayout.Statement>
193+
);
194+
195+
expect(getByText('Statement')).toHaveStyle({ fontSize: '88px' });
196+
});
197+
198+
it('SlideLayout.BigFact should render a slide with fact text', () => {
199+
const { getByText } = renderInDeck(<SlideLayout.BigFact fact={'100%'} />);
200+
201+
expect(getByText('100%')).toBeDefined();
202+
});
203+
204+
it('SlideLayout.BigFact should render a slide with props passed through', () => {
205+
const { getByText } = renderInDeck(
206+
<SlideLayout.BigFact fact={'100%'} factProps={{ fontSize: '88px' }} />
207+
);
208+
209+
expect(getByText('100%')).toHaveStyle({ fontSize: '88px' });
210+
});
211+
212+
it('SlideLayout.BigFact should render a fact with default font size', () => {
213+
const { getByText } = renderInDeck(<SlideLayout.BigFact fact={'100%'} />);
214+
215+
expect(getByText('100%')).toHaveStyle({ fontSize: '250px' });
216+
});
217+
218+
it('SlideLayout.BigFact should render a fact with customizable font size', () => {
219+
const { getByText } = renderInDeck(
220+
<SlideLayout.BigFact fact={'100%'} factFontSize={'150px'} />
221+
);
222+
223+
expect(getByText('100%')).toHaveStyle({ fontSize: '150px' });
224+
});
225+
226+
it('SlideLayout.BigFact should render a slide with fact information if it exists', () => {
227+
const { getByText } = renderInDeck(
228+
<SlideLayout.BigFact fact={'100%'} factInformation={'We earned 100%!'} />
229+
);
230+
231+
expect(getByText('We earned 100%!')).toBeDefined();
232+
});
233+
234+
it('SlideLayout.Quote should render a slide with a quote and attribution text', () => {
235+
const { getByText } = renderInDeck(
236+
<SlideLayout.Quote
237+
quote={'To be, or not to be...'}
238+
attribution={'William Shakespeare'}
239+
/>
240+
);
241+
242+
expect(getByText('To be, or not to be...')).toBeDefined();
243+
expect(getByText('William Shakespeare', { exact: false })).toBeDefined();
244+
});
245+
246+
it('SlideLayout.Quote should render a slide with quote and attribution props passed through', () => {
247+
const { getByText } = renderInDeck(
248+
<SlideLayout.Quote
249+
quote={
250+
"I've learned that people will forget what you said, people will forget what you did, but people will never forget how you made them feel."
251+
}
252+
quoteProps={{ fontSize: '68px' }}
253+
attribution={'Maya Angelou'}
254+
attributionProps={{ fontSize: '48px' }}
255+
/>
256+
);
257+
258+
expect(
259+
getByText(
260+
"I've learned that people will forget what you said, people will forget what you did, but people will never forget how you made them feel."
261+
)
262+
).toHaveStyle({ fontSize: '68px' });
263+
expect(getByText('Maya Angelou', { exact: false })).toHaveStyle({
264+
fontSize: '48px'
265+
});
266+
});
123267
});

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

Lines changed: 125 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
import Slide, { SlideProps } from './slide/slide';
22
import { Box, FlexBox } from './layout-primitives';
33
import { ComponentProps, Fragment, ReactNode } from 'react';
4-
import { Heading, ListItem, OrderedList, UnorderedList } from './typography';
4+
import {
5+
Heading,
6+
Text,
7+
ListItem,
8+
OrderedList,
9+
UnorderedList
10+
} from './typography';
511
import { Appear } from './appear';
612

713
/**
@@ -82,14 +88,127 @@ const List = ({
8288
);
8389
};
8490

91+
/**
92+
* Generic vertically-centered Header layout
93+
*/
94+
const Header = ({
95+
flexBoxProps,
96+
headingProps,
97+
children,
98+
...rest
99+
}: SlideProps & {
100+
flexBoxProps?: ComponentProps<typeof FlexBox>;
101+
headingProps?: ComponentProps<typeof Heading>;
102+
}) => (
103+
<Slide {...rest}>
104+
<FlexBox height="100%" {...flexBoxProps}>
105+
<Heading {...headingProps}>{children}</Heading>
106+
</FlexBox>
107+
</Slide>
108+
);
109+
110+
/**
111+
* Section layout with left aligned text
112+
*/
113+
const Section = ({
114+
sectionProps,
115+
children,
116+
...rest
117+
}: SlideProps & {
118+
sectionProps?: ComponentProps<typeof Heading>;
119+
}) => (
120+
<Header
121+
headingProps={sectionProps}
122+
flexBoxProps={{ justifyContent: 'flex-start' }}
123+
>
124+
{children}
125+
</Header>
126+
);
127+
128+
/**
129+
* Statement layout with centered text
130+
*/
131+
const Statement = ({
132+
statementProps,
133+
children,
134+
...rest
135+
}: SlideProps & {
136+
statementProps?: ComponentProps<typeof Heading>;
137+
}) => <Header headingProps={statementProps}>{children}</Header>;
138+
139+
/**
140+
* Big Fact with optional fact information
141+
*/
142+
const BigFact = ({
143+
fact,
144+
factInformation,
145+
factProps,
146+
factFontSize = '250px',
147+
factInformationProps,
148+
...rest
149+
}: Omit<SlideProps, 'children'> & {
150+
fact: string | ReactNode;
151+
factInformation?: string | ReactNode;
152+
factProps?: ComponentProps<typeof Text>;
153+
factFontSize?: string;
154+
factInformationProps?: ComponentProps<typeof Text>;
155+
}) => (
156+
<Slide {...rest}>
157+
<FlexBox>
158+
<Box>
159+
<Text textAlign="center" fontSize={factFontSize} {...factProps}>
160+
{fact}
161+
</Text>
162+
{factInformation ? (
163+
<Text textAlign="center" {...factInformationProps}>
164+
{factInformation}
165+
</Text>
166+
) : null}
167+
</Box>
168+
</FlexBox>
169+
</Slide>
170+
);
171+
172+
/**
173+
* Quote layout
174+
*/
175+
const Quote = ({
176+
quote,
177+
quoteProps,
178+
attribution,
179+
attributionProps,
180+
...rest
181+
}: Omit<SlideProps, 'children'> & {
182+
quote: string | ReactNode;
183+
quoteProps?: ComponentProps<typeof Text>;
184+
attribution: string | ReactNode;
185+
attributionProps?: ComponentProps<typeof Text>;
186+
}) => (
187+
<Slide {...rest}>
188+
<Box width="100%" margin="auto">
189+
<Text fontSize="85px" {...quoteProps}>
190+
{quote}
191+
</Text>
192+
<Text fontSize="36px" padding={'0em 0em 0em 1em'} {...attributionProps}>
193+
&ndash;{attribution}
194+
</Text>
195+
</Box>
196+
</Slide>
197+
);
85198
/**
86199
* Layouts to consider:
87200
* - Image (left, right, full bleed?)
88201
* - Intro
89-
* - Quote
90-
* - Section
91-
* - Statement?
92-
* - Big fact?
202+
* - Code Snippet (syntax highlighting)
93203
*/
94204

95-
export default { Full, Center, TwoColumn, List };
205+
export default {
206+
Full,
207+
Center,
208+
TwoColumn,
209+
List,
210+
Section,
211+
BigFact,
212+
Quote,
213+
Statement
214+
};

0 commit comments

Comments
 (0)