Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/little-peaches-jump.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'spectacle': minor
---

feat: Add new Slide Layouts: Section, Statement, Big fact, Quote to expand basic layout creation.
44 changes: 44 additions & 0 deletions docs/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -404,3 +404,47 @@ A layout with a list and an optional title for if you want to quickly display a
| `items` | `ReactNode[]` | ✅ | `['Hello', <Text>World</Text>]` |
| `animateListItems` | `boolean` | ❌ | `true` |
| `listProps` | [List Props](#typography-tags) | ❌ | `{ backgroundColor: 'purple' }` |


### `SlideLayout.Section`

A vertically-centered left-aligned section title layout for if you want title page for a new section.

| Props | Type | Required | Example |
|------------------------|---------------------------------|----------|--------------------------|
| `...slideProps` | [Slide Props](#slide) | ❌ | |
| `sectionProps` | [Text Props](#typography-tags) | ❌ | { fontSize: "48px" } |

### `SlideLayout.Statement`

A vertically-centered center-aligned statement for if you want to make a statement.

| Props | Type | Required | Example |
|------------------------|---------------------------------|----------|--------------------------|
| `...slideProps` | [Slide Props](#slide) | ❌ | |
| `statementProps` | [Text Props](#typography-tags) | ❌ | { fontSize: "48px" } |

### `SlideLayout.BigFact`

A centered Big Fact layout for if you want to present a fact in a large font.

| Props | Type | Required | Example | Default |
|---------------------------|---------------------------------|----------|------------------------|---------|
| `...slideProps` | [Slide Props](#slide) | ❌ | | |
| `fact` | `string | ReactNode` | ✅ | `100%` | |
| `factInformation` | `string | ReactNode` | ❌ | `Fact information` | |
| `factProps` | [Text Props](#typography-tags) | ❌ | { fontSize: "100px" } | |
| `factInformationProps` | [Text Props](#typography-tags) | ❌ | { fontSize: "48px" } | |
| `factFontSize` | `string` | ❌ | `150px` |`250px` |

### `SlideLayout.Quote`

A vertically-centered Quote layout for if you want to present a quote and attribute it to someone.

| Props | Type | Required | Example |
|-----------------------|---------------------------------|----------|------------------------|
| `...slideProps` | [Slide Props](#slide) | ❌ | |
| `quote` | `string | ReactNode` | ✅ | `To be, or not to be` |
| `attribution` | `string | ReactNode` | ✅ | `William Shakespeare` |
| `quoteProps` | [Text Props](#typography-tags) | ❌ | { fontSize: "100px" } |
| `attributionProps` | [Text Props](#typography-tags) | ❌ | { fontSize: "48px" } |
144 changes: 144 additions & 0 deletions packages/spectacle/src/components/slide-layout.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,148 @@ describe('SlideLayout', () => {

expect(queryAllByTestId('AppearElement')).toHaveLength(3);
});

it('SlideLayout.Section should render a section title', () => {
const { getByText } = renderInDeck(
<SlideLayout.Section>{'Section title'}</SlideLayout.Section>
);

expect(getByText('Section title')).toBeDefined();
});

it('SlideLayout.Section should render a section title within a react node', () => {
const { getByText } = renderInDeck(
<SlideLayout.Section>
{
<>
Hello<em>World!</em>
</>
}
</SlideLayout.Section>
);

expect(getByText('World!')).toBeDefined();
});

it('SlideLayout.Section should render a section slide with props passed through', () => {
const { getByText } = renderInDeck(
<SlideLayout.Section sectionProps={{ fontSize: '68px' }}>
{'Section title'}
</SlideLayout.Section>
);

expect(getByText('Section title')).toHaveStyle({ fontSize: '68px' });
});

it('SlideLayout.Section should render a section title in a left aligned flexbox', () => {
const { getByText } = renderInDeck(
<SlideLayout.Section>{'Section title'}</SlideLayout.Section>
);

expect(getByText('Section title').parentElement).toHaveStyle({
justifyContent: 'flex-start'
});
});

it('SlideLayout.Statement should render statement text', () => {
const { getByText } = renderInDeck(
<SlideLayout.Statement>{'Statement'}</SlideLayout.Statement>
);

expect(getByText('Statement')).toBeDefined();
});

it('SlideLayout.Statement should render statement text within a react node', () => {
const { getByText } = renderInDeck(
<SlideLayout.Statement>
{
<>
Hello<em>World!</em>
</>
}
</SlideLayout.Statement>
);

expect(getByText('World!')).toBeDefined();
});

it('SlideLayout.Statement should render a statement slide with props passed through', () => {
const { getByText } = renderInDeck(
<SlideLayout.Statement statementProps={{ fontSize: '88px' }}>
{'Statement'}
</SlideLayout.Statement>
);

expect(getByText('Statement')).toHaveStyle({ fontSize: '88px' });
});

it('SlideLayout.BigFact should render a slide with fact text', () => {
const { getByText } = renderInDeck(<SlideLayout.BigFact fact={'100%'} />);

expect(getByText('100%')).toBeDefined();
});

it('SlideLayout.BigFact should render a slide with props passed through', () => {
const { getByText } = renderInDeck(
<SlideLayout.BigFact fact={'100%'} factProps={{ fontSize: '88px' }} />
);

expect(getByText('100%')).toHaveStyle({ fontSize: '88px' });
});

it('SlideLayout.BigFact should render a fact with default font size', () => {
const { getByText } = renderInDeck(<SlideLayout.BigFact fact={'100%'} />);

expect(getByText('100%')).toHaveStyle({ fontSize: '250px' });
});

it('SlideLayout.BigFact should render a fact with customizable font size', () => {
const { getByText } = renderInDeck(
<SlideLayout.BigFact fact={'100%'} factFontSize={'150px'} />
);

expect(getByText('100%')).toHaveStyle({ fontSize: '150px' });
});

it('SlideLayout.BigFact should render a slide with fact information if it exists', () => {
const { getByText } = renderInDeck(
<SlideLayout.BigFact fact={'100%'} factInformation={'We earned 100%!'} />
);

expect(getByText('We earned 100%!')).toBeDefined();
});

it('SlideLayout.Quote should render a slide with a quote and attribution text', () => {
const { getByText } = renderInDeck(
<SlideLayout.Quote
quote={'To be, or not to be...'}
attribution={'William Shakespeare'}
/>
);

expect(getByText('To be, or not to be...')).toBeDefined();
expect(getByText('William Shakespeare', { exact: false })).toBeDefined();
});

it('SlideLayout.Quote should render a slide with quote and attribution props passed through', () => {
const { getByText } = renderInDeck(
<SlideLayout.Quote
quote={
"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."
}
quoteProps={{ fontSize: '68px' }}
attribution={'Maya Angelou'}
attributionProps={{ fontSize: '48px' }}
/>
);

expect(
getByText(
"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."
)
).toHaveStyle({ fontSize: '68px' });
expect(getByText('Maya Angelou', { exact: false })).toHaveStyle({
fontSize: '48px'
});
});
});
131 changes: 125 additions & 6 deletions packages/spectacle/src/components/slide-layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import Slide, { SlideProps } from './slide/slide';
import { Box, FlexBox } from './layout-primitives';
import { ComponentProps, Fragment, ReactNode } from 'react';
import { Heading, ListItem, OrderedList, UnorderedList } from './typography';
import {
Heading,
Text,
ListItem,
OrderedList,
UnorderedList
} from './typography';
import { Appear } from './appear';

/**
Expand Down Expand Up @@ -82,14 +88,127 @@ const List = ({
);
};

/**
* Generic vertically-centered Header layout
*/
const Header = ({
flexBoxProps,
headingProps,
children,
...rest
}: SlideProps & {
flexBoxProps?: ComponentProps<typeof FlexBox>;
headingProps?: ComponentProps<typeof Heading>;
}) => (
<Slide {...rest}>
<FlexBox height="100%" {...flexBoxProps}>
<Heading {...headingProps}>{children}</Heading>
</FlexBox>
</Slide>
);

/**
* Section layout with left aligned text
*/
const Section = ({
sectionProps,
children,
...rest
}: SlideProps & {
sectionProps?: ComponentProps<typeof Heading>;
}) => (
<Header
headingProps={sectionProps}
flexBoxProps={{ justifyContent: 'flex-start' }}
>
{children}
</Header>
);

/**
* Statement layout with centered text
*/
const Statement = ({
statementProps,
children,
...rest
}: SlideProps & {
statementProps?: ComponentProps<typeof Heading>;
}) => <Header headingProps={statementProps}>{children}</Header>;

/**
* Big Fact with optional fact information
*/
const BigFact = ({
fact,
factInformation,
factProps,
factFontSize = '250px',
factInformationProps,
...rest
}: Omit<SlideProps, 'children'> & {
fact: string | ReactNode;
factInformation?: string | ReactNode;
factProps?: ComponentProps<typeof Text>;
factFontSize?: string;
factInformationProps?: ComponentProps<typeof Text>;
}) => (
<Slide {...rest}>
<FlexBox>
<Box>
<Text textAlign="center" fontSize={factFontSize} {...factProps}>
{fact}
</Text>
{factInformation ? (
<Text textAlign="center" {...factInformationProps}>
{factInformation}
</Text>
) : null}
</Box>
</FlexBox>
</Slide>
);

/**
* Quote layout
*/
const Quote = ({
quote,
quoteProps,
attribution,
attributionProps,
...rest
}: Omit<SlideProps, 'children'> & {
quote: string | ReactNode;
quoteProps?: ComponentProps<typeof Text>;
attribution: string | ReactNode;
attributionProps?: ComponentProps<typeof Text>;
}) => (
<Slide {...rest}>
<Box width="100%" margin="auto">
<Text fontSize="85px" {...quoteProps}>
{quote}
</Text>
<Text fontSize="36px" padding={'0em 0em 0em 1em'} {...attributionProps}>
&ndash;{attribution}
</Text>
</Box>
</Slide>
);
/**
* Layouts to consider:
* - Image (left, right, full bleed?)
* - Intro
* - Quote
* - Section
* - Statement?
* - Big fact?
* - Code Snippet (syntax highlighting)
*/

export default { Full, Center, TwoColumn, List };
export default {
Full,
Center,
TwoColumn,
List,
Section,
BigFact,
Quote,
Statement
};