Skip to content

Commit f3fd6f7

Browse files
authored
Fix: theme.backdropStyle overrides (#1231)
* chore: fix up example starting scripts * fix: prevent theme.backdropStyle styles for background from being overridden * docs: add documentation for backdrop customization via theme * chore: workaround to keep BroadcastChannel from stalling test exiting * docs: expand a bit on what a backdrop is
1 parent bbfaa35 commit f3fd6f7

File tree

8 files changed

+84
-34
lines changed

8 files changed

+84
-34
lines changed

.changeset/heavy-impalas-stare.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'spectacle': patch
3+
---
4+
5+
prevent theme.backdropStyle styles for background from being overridden

docs/themes.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ Common CSS properties are divided into theme keys, which you can override in you
5858
| `shadows` | `box-shadow`, `text-shadow` |
5959
| `zIndices` | `z-index` |
6060

61+
Additionally, there are keys for assorted overrides for the presentation:
62+
63+
| Theme Key | Type | Description |
64+
| ---------------- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
65+
| `backdropStyle` | `React.CSSProperties` | Set styles for the backdrop (the edge-to-edge area behind the slides) similar to React `style` props, e.g., `{ backgroundColor: '#FFF' }`. Defaults to `{ position: 'fixed', top: 0, left: 0, width: '100vw', height: '100vh' }` |
66+
| `Backdrop` | `React.ElementType` | Replace the default backdrop component (which appears behind the slides) with a completely custom component or tag name, e.g., `'main'` or `MyCustomComponent`. Defaults to `'div'`. |
67+
6168
## Deck Templates
6269

6370
A template in Spectacle is a fixed overlay of components that are presented on every slide. They are similar to masters in Keynote or PowerPoint. It’s a function prop that has a single optional config object containing the current slide and total slide count and returns a React Node.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@
5656
"start:js": "concurrently --raw pnpm:build:spectacle:esm:watch pnpm:build:spectacle:types:watch \"pnpm run --filter ./examples/js start\"",
5757
"start:md": "concurrently --raw pnpm:build:spectacle:esm:watch pnpm:build:spectacle:types:watch \"pnpm run --filter ./examples/md start\"",
5858
"start:ts": "concurrently --raw pnpm:build:spectacle:esm:watch pnpm:build:spectacle:types:watch \"pnpm run --filter ./examples/typescript start\"",
59-
"start:one-page": "concurrently --raw pnpm:build:spectacle:dev:watch pnpm:build:spectacle:dev:watch",
60-
"start:examples": "concurrently --raw pnpm:build:spectacle:esm:watch pnpm:build:spectacle:dev:watch pnpm:build:spectacle:dev:watch \"pnpm run --parallel --filter \\\"./examples/*\\\" start\"",
59+
"start:one-page": "concurrently --raw pnpm:build:spectacle:dev:watch pnpm:build:spectacle:types:watch",
60+
"start:examples": "concurrently --raw pnpm:build:spectacle:esm:watch pnpm:build:spectacle:dev:watch pnpm:build:spectacle:types:watch \"pnpm run --parallel --filter \\\"./examples/*\\\" start\"",
6161
"start:create-spectacle": "pnpm run --filter ./packages/create-spectacle build --watch",
6262
"clean:build": "rimraf \"{packages,examples}/*/{es,lib,dist}\" packages/create-spectacle/bin",
6363
"clean:cache": "wireit",
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import Deck from './index';
2+
import Slide from '../slide/slide';
3+
import { render } from '@testing-library/react';
4+
import { CSSObject } from 'styled-components';
5+
6+
describe('<Deck />', () => {
7+
it('should allow for backdrop color overrides from theme prop', () => {
8+
const deckWithStyle = (
9+
backdropStyle: CSSObject,
10+
Backdrop?: React.ElementType
11+
) => (
12+
<Deck className="backdrop" theme={{ backdropStyle, Backdrop }}>
13+
<Slide>Hi</Slide>
14+
</Deck>
15+
);
16+
let { container, rerender } = render(deckWithStyle({ padding: 16 }));
17+
18+
expect(container.querySelector('.backdrop')).toHaveStyle({
19+
backgroundColor: 'black'
20+
});
21+
22+
rerender(deckWithStyle({ backgroundColor: 'blue' }));
23+
expect(container.querySelector('.backdrop')).toHaveStyle({
24+
backgroundColor: 'blue'
25+
});
26+
27+
rerender(deckWithStyle({ background: 'red' }));
28+
expect(container.querySelector('.backdrop')).toHaveStyle({
29+
backgroundColor: 'red'
30+
});
31+
32+
rerender(deckWithStyle({}, 'section'));
33+
expect(container.querySelector('.backdrop')).not.toHaveStyle({
34+
backgroundColor: 'black'
35+
});
36+
});
37+
});

packages/spectacle/src/components/deck/deck.tsx

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -400,21 +400,16 @@ export const DeckInternal = forwardRef<DeckRef, DeckInternalProps>(
400400
//
401401
// Yes, this is slightly awkward, but IMO adding an additional `<div>` element
402402
// would be even more awkward.
403-
let useFallbackBackdropStyle = true;
404-
const backdropStyle = themeProvidedBackdropStyle;
405-
let BackdropComponent = 'div' as ElementType;
406-
if (userProvidedBackdropStyle) {
407-
Object.assign(backdropStyle, userProvidedBackdropStyle);
408-
if (backdropStyle['background'] || backdropStyle['backgroundColor']) {
409-
useFallbackBackdropStyle = false;
410-
}
411-
}
412-
if (UserProvidedBackdropComponent) {
413-
BackdropComponent = UserProvidedBackdropComponent;
414-
useFallbackBackdropStyle = false;
415-
}
403+
const backdropStyle = {
404+
...themeProvidedBackdropStyle,
405+
...userProvidedBackdropStyle
406+
};
407+
const BackdropComponent = UserProvidedBackdropComponent || 'div';
408+
416409
if (
417-
useFallbackBackdropStyle &&
410+
!backdropStyle['background'] &&
411+
!backdropStyle['backgroundColor'] &&
412+
!UserProvidedBackdropComponent &&
418413
!suppressBackdropFallback &&
419414
!themeSuppressBackdropFallback
420415
) {
@@ -535,6 +530,11 @@ type MarkdownThemeOverrides = {
535530
type BackdropOverrides = {
536531
Backdrop?: ElementType;
537532
backdropStyle?: CSSObject;
533+
/**
534+
* @deprecated set a value to one of the `Backdrop`,
535+
* `backdropStyle.background`, or `backdropStyle.backgroundColor` properties
536+
* inside the `theme` prop object instead
537+
*/
538538
suppressBackdropFallback?: boolean;
539539
};
540540

@@ -556,6 +556,11 @@ export type DeckProps = {
556556
printScale?: number;
557557
overviewScale?: number;
558558
transition?: SlideTransition;
559+
/**
560+
* @deprecated set a value to one of the `Backdrop`,
561+
* `backdropStyle.background`, or `backdropStyle.backgroundColor` properties
562+
* inside the `theme` prop object instead
563+
*/
559564
suppressBackdropFallback?: boolean;
560565
backgroundImage?: string;
561566
};
@@ -573,6 +578,7 @@ export type DeckInternalProps = DeckProps & {
573578
disableInteractivity?: boolean;
574579
useAnimations?: boolean;
575580
notePortalNode?: HTMLDivElement | null;
581+
/** @deprecated use the backdropStyle property inside the `theme` prop object instead */
576582
backdropStyle?: Partial<CSSStyleDeclaration>;
577583
onActiveStateChange?: (activeView: DeckView) => void;
578584
backgroundImage?: string;

packages/spectacle/src/components/markdown/markdown.test.tsx

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,6 @@ import { Heading } from '../typography';
55
import Slide from '../slide/slide';
66
import { render } from '@testing-library/react';
77

8-
jest.mock('../../hooks/use-broadcast-channel', () => {
9-
return {
10-
__esModule: true,
11-
default: function useBroadcastChannel() {
12-
return [() => {}];
13-
}
14-
};
15-
});
16-
178
const mountInsideDeck = (tree: ReactElement) => {
189
return render(<Deck>{tree}</Deck>);
1910
};

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

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,6 @@ import Deck from './deck';
44
import SlideLayout from './slide-layout';
55
import { Heading, Text } from './typography';
66

7-
jest.mock('../hooks/use-broadcast-channel', () => {
8-
return {
9-
__esModule: true,
10-
default: function useBroadcastChannel() {
11-
return [() => {}];
12-
}
13-
};
14-
});
15-
167
const renderInDeck = (tree: ReactElement | JSX.Element) =>
178
render(<Deck>{tree}</Deck>);
189

packages/spectacle/src/test-utils/test-setup.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,16 @@ window.HTMLElement.prototype.getClientRects = (): any => [
1313
jest.mock('use-resize-observer', () => {
1414
return jest.requireActual('use-resize-observer/polyfilled');
1515
});
16+
17+
jest.mock('broadcast-channel', () => {
18+
const bcExport = jest.requireActual('broadcast-channel');
19+
const ActualBroadcastChannel = bcExport.BroadcastChannel;
20+
21+
// Wrap the BroadcastChannel constructor so it always uses the `simulate`
22+
// mode. This prevents tests from hanging after mounting a Deck.
23+
bcExport.BroadcastChannel = function (name: string) {
24+
return new ActualBroadcastChannel(name, { type: 'simulate' });
25+
};
26+
27+
return bcExport;
28+
});

0 commit comments

Comments
 (0)