Repository: FormidableLabs/spectacle
Pull request #946 (2020-09-10 15:16:35)

Feature/templates progress v7
* Adds support for Templates
* Fixes Progress component
* Adds standard template to Example JS

![image](https://user-images.githubusercontent.com/1738349/92752547-a5083a80-f34e-11ea-800a-06fdee6ada5f.png)



Patch:
diff --git a/.eslintrc b/.eslintrc
index 6eda777..8052bb9 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -7,7 +7,9 @@
     "plugin:react/recommended",
     "plugin:react-hooks/recommended"
   ],
-  "plugins": ["prettier"],
+  "plugins": [
+    "prettier"
+  ],
   "env": {
     "browser": true,
     "commonjs": true,
@@ -33,7 +35,15 @@
     }
   },
   "rules": {
-    "prettier/prettier": ["error"],
-    "quotes": ["error", "single", { "allowTemplateLiterals": true }]
+    "prettier/prettier": [
+      "error"
+    ],
+    "quotes": [
+      "error",
+      "single",
+      {
+        "allowTemplateLiterals": true
+      }
+    ]
   }
-}
+}
\ No newline at end of file
diff --git a/examples/js/index.js b/examples/js/index.js
index 61cb0ad..b5ee015 100644
--- a/examples/js/index.js
+++ b/examples/js/index.js
@@ -9,6 +9,8 @@ import {
   CodeSpan,
   OrderedList,
   ListItem,
+  FullScreen,
+  Progress,
   Appear,
   Slide,
   Deck,
@@ -16,9 +18,9 @@ import {
   Grid,
   Box,
   Image,
-  CodePane
+  CodePane,
+  Notes
 } from 'spectacle';
-import Notes from '../../src/components/notes';
 
 const formidableLogo =
   'https://avatars2.githubusercontent.com/u/5078602?s=280&v=4';
@@ -33,7 +35,21 @@ const theme = {
 // SPECTACLE_CLI_THEME_END
 
 // SPECTACLE_CLI_TEMPLATE_START
-// TODO(7): Insert template definition here and integrate below in code.
+const template = () => (
+  <FlexBox
+    justifyContent="space-between"
+    position="absolute"
+    bottom={0}
+    width={1}
+  >
+    <Box padding="0 1em">
+      <FullScreen />
+    </Box>
+    <Box padding="1em">
+      <Progress />
+    </Box>
+  </FlexBox>
+);
 // SPECTACLE_CLI_TEMPLATE_END
 
 function SlideFragments() {
@@ -57,7 +73,7 @@ function SlideFragments() {
 
 function Presentation() {
   return (
-    <Deck theme={theme}>
+    <Deck theme={theme} template={template}>
       <Slide>
         <FlexBox height="100%">
           <SpectacleLogo size={500} />
diff --git a/examples/one-page.html b/examples/one-page.html
index 5d667ff..7956ddd 100644
--- a/examples/one-page.html
+++ b/examples/one-page.html
@@ -26,6 +26,8 @@
         CodeSpan,
         OrderedList,
         ListItem,
+        FullScreen,
+        Progress,
         Appear,
         Slide,
         Deck,
@@ -33,7 +35,8 @@
         Grid,
         Box,
         Image,
-        CodePane
+        CodePane,
+        Notes
       } = Spectacle;
 
       import htm from 'https://unpkg.com/htm@^3?module';
@@ -50,9 +53,18 @@
       // SPECTACLE_CLI_THEME_END
 
       // SPECTACLE_CLI_TEMPLATE_START
-      // TODO(7): Insert template definition here and integrate below in code.
-
+      const template = () => html`
+        <${FlexBox} justifyContent="space-between" position="absolute" bottom=${0} width=${1}>
+          <${Box} padding="0 1em">
+            <${FullScreen} />
+          </${Box}>
+          <${Box} padding="1em">
+            <${Progress} />
+          </${Box}>
+        </${FlexBox}>
+      `;
       // SPECTACLE_CLI_TEMPLATE_END
+
       function SlideFragments() {
         return html`
         <${Slide}>
@@ -72,11 +84,16 @@
 
       function Presentation() {
         return html`
-        <${Deck} theme=${theme}>
+        <${Deck} theme=${theme} template=${template}>
           <${Slide}>
             <${FlexBox} height="100%">
               <${SpectacleLogo} size=${500} />
             </${FlexBox}>
+            <${Notes}>Spectacle supports notes per slide.<ol>
+                <li>Notes can now be HTML markup!</li>
+                <li>Lists can make it easier to make points.</li>
+              </ol>
+            </${Notes}>
           </${Slide}>
           <${Slide}>
             <${FlexBox} height="100%" flexDirection="column">
diff --git a/src/components/deck/deck.js b/src/components/deck/deck.js
index 926e434..75f8cf7 100644
--- a/src/components/deck/deck.js
+++ b/src/components/deck/deck.js
@@ -11,7 +11,7 @@ import { mergeTheme } from '../../theme';
 import * as queryStringMapFns from '../../location-map-fns/query-string';
 
 export const DeckContext = React.createContext();
-const noop = () => {};
+const noop = () => { };
 
 const Deck = React.forwardRef(
   (
@@ -21,6 +21,7 @@ const Deck = React.forwardRef(
       backdropStyle: userProvidedBackdropStyle,
       overviewMode = false,
       overviewScale = 0.25,
+      template,
       theme: {
         slideDimensions: [nativeSlideWidth, nativeSlideHeight] = [1366, 768],
         Backdrop: UserProvidedBackdropComponent,
@@ -109,9 +110,9 @@ const Deck = React.forwardRef(
       disableInteractivity
         ? {}
         : {
-            left: () => stepBackward(),
-            right: () => stepForward()
-          },
+          left: () => stepBackward(),
+          right: () => stepForward()
+        },
       []
     );
 
@@ -305,7 +306,8 @@ const Deck = React.forwardRef(
               advanceSlide,
               regressSlide,
               commitTransition,
-              cancelTransition
+              cancelTransition,
+              template
             }}
           >
             <div ref={setPlaceholderContainer} style={{ display: 'none' }}>
diff --git a/src/components/progress.js b/src/components/progress.js
index 4a0463a..4c2e775 100644
--- a/src/components/progress.js
+++ b/src/components/progress.js
@@ -1,7 +1,7 @@
 import * as React from 'react';
 import propTypes from 'prop-types';
 import styled from 'styled-components';
-import { DeckContext } from '../hooks/use-deck';
+import { DeckContext } from './deck/deck';
 
 export const Circle = styled('div')`
   width: ${({ size }) => size}px;
@@ -22,18 +22,23 @@ const Container = styled('div')`
 `;
 
 const Progress = props => {
-  const { numberOfSlides, state, goToSlide } = React.useContext(DeckContext);
+  const { slideCount, skipTo, activeView } = React.useContext(DeckContext);
   return (
     <Container className="spectacle-progress-indicator">
-      {Array(numberOfSlides)
+      {Array(slideCount)
         .fill(0)
         .map((_, idx) => (
           <Circle
             key={`progress-circle-${idx}`}
             color={props.color}
-            active={state.currentSlide === idx}
+            active={activeView.slideIndex === idx}
             size={props.size}
-            onClick={() => goToSlide(idx)}
+            onClick={() =>
+              skipTo({
+                slideIndex: idx,
+                stepIndex: 0
+              })
+            }
             data-testid="Progress Circle"
           />
         ))}
diff --git a/src/components/slide/slide.js b/src/components/slide/slide.js
index 3f87d51..2fe0aa9 100644
--- a/src/components/slide/slide.js
+++ b/src/components/slide/slide.js
@@ -9,7 +9,7 @@ import { useSlide } from '../../hooks/use-slides';
 import { useCollectSteps } from '../../hooks/use-steps';
 import { GOTO_FINAL_STEP } from '../../hooks/use-deck-state';
 
-const noop = () => {};
+const noop = () => { };
 
 export const SlideContext = React.createContext();
 
@@ -56,6 +56,15 @@ const SlideWrapper = styled('div')(
   `
 );
 
+const TemplateWrapper = styled('div')`
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  pointer-events: none;
+`;
+
 export default function Slide({
   id: userProvidedId,
   children,
@@ -67,6 +76,7 @@ export default function Slide({
   backgroundSize,
   padding,
   textColor,
+  template,
   className = ''
 }) {
   if (React.useContext(SlideContext)) {
@@ -93,7 +103,8 @@ export default function Slide({
     advanceSlide,
     regressSlide,
     commitTransition,
-    cancelTransition
+    cancelTransition,
+    template: deckTemplate
   } = React.useContext(DeckContext);
 
   const handleClick = React.useCallback(() => {
@@ -117,11 +128,8 @@ export default function Slide({
   // we haven't gotten to it yet, none of them should be visible. (This helps us
   // handle slides which are exiting but which are still visible while
   // animated.)
-  const internalStepIndex = isActive
-    ? activeView.stepIndex
-    : isPassed
-    ? Infinity
-    : -Infinity;
+  const infinityDirection = isPassed ? Infinity : -Infinity;
+  const internalStepIndex = isActive ? activeView.stepIndex : infinityDirection;
 
   React.useEffect(() => {
     if (!isActive) return;
@@ -265,6 +273,14 @@ export default function Slide({
                 backgroundSize={backgroundSize}
                 color={textColor}
               >
+                <TemplateWrapper>
+                  {(typeof template === 'function' ||
+                    typeof deckTemplate === 'function') &&
+                    (template || deckTemplate)({
+                      slideNumber: 0,
+                      numberOfSlides: 0
+                    })}
+                </TemplateWrapper>
                 <SlideWrapper padding={padding}>{children}</SlideWrapper>
               </SlideContainer>
             </animated.div>,
@@ -286,7 +302,8 @@ Slide.propTypes = {
   backgroundSize: PropTypes.string,
   children: PropTypes.node.isRequired,
   padding: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
-  textColor: PropTypes.string
+  textColor: PropTypes.string,
+  template: PropTypes.func
 };
 
 Slide.defaultProps = {


Test patch:
diff --git a/src/components/progress.test.js b/src/components/progress.test.js
index 2b5b887..64cb4c5 100644
--- a/src/components/progress.test.js
+++ b/src/components/progress.test.js
@@ -4,7 +4,7 @@ import Enzyme, { mount } from 'enzyme';
 import Adapter from 'enzyme-adapter-react-16';
 import { ThemeProvider } from 'styled-components';
 
-import { DeckContext } from '../hooks/use-deck';
+import { DeckContext } from './deck/deck';
 import defaultTheme from '../theme/default-theme';
 import Progress, { Circle } from './progress';
 
@@ -27,9 +27,9 @@ const mountWithContext = (tree, context) => {
 describe('<Progress />', () => {
   it('should render the right amount of circles', () => {
     const wrapper = mountWithContext(<Progress />, {
-      numberOfSlides: 5,
-      state: {
-        currentSlide: 0
+      slideCount: 5,
+      activeView: {
+        slideIndex: 0
       }
     });
     expect(wrapper.find(Circle).length).toBe(5);
@@ -37,9 +37,9 @@ describe('<Progress />', () => {
 
   it('should render the right amount of circles with the current circle in the active state', () => {
     const wrapper = mountWithContext(<Progress />, {
-      numberOfSlides: 5,
-      state: {
-        currentSlide: 4
+      slideCount: 5,
+      activeView: {
+        slideIndex: 4
       }
     });
     expect(