All files / publisher/tour tourStepCard.tsx

100% Statements 16/16
100% Branches 10/10
100% Functions 1/1
100% Lines 16/16

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115                                                        33x   33x     33x 33x   33x     16x       16x   4x       4x   10x       10x   3x       3x         33x   33x   33x                                                                                        
import { ReactNode } from "react";
 
import type { Step } from "../listing/types";
 
type Props = {
  steps: Step[];
  currentStepIndex: number;
  mask: {
    top: number;
    bottom: number;
    left: number;
    right: number;
  };
  onFinishClick: () => void;
  onSkipClick: () => void;
  onNextClick: () => void;
  onPrevClick: () => void;
};
 
export default function TourStepCard({
  steps,
  currentStepIndex,
  mask,
  onFinishClick,
  onSkipClick,
  onNextClick,
  onPrevClick,
}: Props): ReactNode {
  const step = steps[currentStepIndex];
 
  let tooltipStyle = {};
 
  // for positioning relative to right/bottom of the screen
  const overlayHeight = document.body.clientHeight;
  const overlayWidth = document.body.clientWidth;
 
  switch (step.position) {
    // TODO: support for all available tooltip positions
    case "bottom-left":
      tooltipStyle = {
        top: mask.bottom,
        left: mask.left,
      };
      break;
    case "bottom-right":
      tooltipStyle = {
        top: mask.bottom,
        right: overlayWidth - mask.right,
      };
      break;
    case "top-left":
      tooltipStyle = {
        bottom: overlayHeight - mask.top,
        left: mask.left,
      };
      break;
    case "top-right":
      tooltipStyle = {
        bottom: overlayHeight - mask.top,
        right: overlayWidth - mask.right,
      };
      break;
  }
 
  // step content is controlled by us and passed as data to component directly
  // so it should be safe to set it via dangerouslySetInnerHTML
  const content = { __html: step.content };
 
  const isLastStep = currentStepIndex === steps.length - 1;
 
  return (
    <div
      className={`p-card--tour is-tooltip--${step.position}`}
      style={tooltipStyle}
    >
      <h4>{step.title}</h4>
      <div
        className="p-card--tour__content"
        dangerouslySetInnerHTML={content}
      />
 
      <p className="p-tour-controls">
        {!isLastStep && (
          <span>
            Done? <a onClick={onSkipClick}>Skip tour</a>.
          </span>
        )}
 
        <small className="p-tour-controls__step">
          {currentStepIndex + 1}/{steps.length}
        </small>
        <span className="p-tour-controls__buttons">
          <button
            disabled={currentStepIndex === 0}
            onClick={onPrevClick}
            className="p-button has-icon u-no-margin--bottom"
          >
            <i className="p-icon--chevron-up is-prev">Previous step</i>
          </button>
          <button
            onClick={isLastStep ? onFinishClick : onNextClick}
            className="p-button--positive has-icon u-no-margin--bottom u-no-margin--right"
          >
            {isLastStep ? (
              "Finish tour"
            ) : (
              <i className="p-icon--chevron-up is-light is-next">Next step</i>
            )}
          </button>
        </span>
      </p>
    </div>
  );
}