Skip to content

Commit

Permalink
feat(CoachmarkStack): convert to .tsx
Browse files Browse the repository at this point in the history
  • Loading branch information
emyarod committed May 22, 2024
1 parent e3af943 commit d41097a
Showing 1 changed file with 93 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import React, {
useRef,
useState,
useCallback,
MutableRefObject,
ReactNode,
} from 'react';
import { createPortal } from 'react-dom';

Expand All @@ -36,6 +38,70 @@ import { CoachmarkTagline } from '../Coachmark/CoachmarkTagline';
import { CoachmarkContext } from '../Coachmark/utils/context';
import { COACHMARK_OVERLAY_KIND } from '../Coachmark/utils/enums';

type Media =
| {
render?: () => ReactNode;
filePaths?: never;
}
| {
render?: never;
filePaths?: string[];
};
interface CoachmarkStackProps {
/**
* CoachmarkStack should use a single CoachmarkOverlayElements component as a child.
*/
children: React.ReactNode;
/**
* Provide an optional class to be applied to the containing node.
*/
className?: string;
/**
* The label for the button that will close the Stack
*/
closeButtonLabel?: string;
// Pass through to CoachmarkStackHome
/**
* The description of the Coachmark.
*/
description: React.ReactNode;
/**
* The object describing an image in one of two shapes.
*
* If a single media element is required, use `{render}`.
*
* If a stepped animation is required, use `{filePaths}`.
*
* @see {@link MEDIA_PROP_TYPE}.
*/
media: Media;
/**
* The labels used to link to the stackable Coachmarks.
*/
navLinkLabels: string[];
/**
* Function to call when the CoachmarkStack closes.
*/
onClose: () => void;
/**
* Where in the DOM to render the stack.
* The default is `document.body`.
*/
portalTarget?: string;
/**
* The tagline title which will be fixed to the bottom right of the window and will serve as the display trigger.
*/
tagline: string;
/**
* Determines the theme of the component.
*/
theme?: 'light' | 'dark';
/**
* The title of the Coachmark.
*/
title: string;
}

const defaults = {
onClose: () => {},
// Pass through to CoachmarkStackHome
Expand All @@ -55,7 +121,10 @@ const defaults = {
* user to gain understanding of the product's main value and discover new use cases.
* This variant allows the stacking of multiple coachmark overlays to be displayed by interacting with the tagline.
*/
export let CoachmarkStack = React.forwardRef(
export let CoachmarkStack = React.forwardRef<
HTMLDivElement,
CoachmarkStackProps
>(
(
{
children,
Expand All @@ -65,7 +134,7 @@ export let CoachmarkStack = React.forwardRef(
description,
media,
navLinkLabels,
portalTarget = defaults.portalTarget,
portalTarget,
closeButtonLabel,
tagline,
theme = defaults.theme,
Expand All @@ -77,18 +146,18 @@ export let CoachmarkStack = React.forwardRef(
const portalNode = portalTarget
? document.querySelector(portalTarget) ?? document.querySelector('body')
: document.querySelector('body');
const stackHomeRef = useRef();
const stackedCoachmarkRefs = useRef([]);
const stackHomeRef = useRef<HTMLDivElement>();
const stackedCoachmarkRefs = useRef<HTMLDivElement[]>([]);
const [isOpen, setIsOpen] = useState(false);
// selectedItemNumber -1 = parent close button was clicked, remove entire stack
// selectedItemNumber 0 = (default) the parent is visible, all children are hidden
// selectedItemNumber 1+ = a child is visible and stacked atop the parent
const [selectedItemNumber, setSelectedItemNumber] = useState(0);
// // The parent height and width values to return to after unstacked
const [parentHeight, setParentHeight] = useState(null);
const [parentHeight, setParentHeight] = useState<number>();
// parent height = child height when stacked behind a child that is shorter
const childArray = Children.toArray(children);
const mountedRef = useRef();
const mountedRef = useRef<boolean>();
// same value as CSS animation speed
const delayMs = 240;

Expand Down Expand Up @@ -169,17 +238,23 @@ export let CoachmarkStack = React.forwardRef(
if (!parentHeight) {
return;
}
stackHomeRef.current.style.height = `${parentHeight}px`;
if (stackHomeRef.current) {
stackHomeRef.current.style.height = `${parentHeight}px`;
}
if (!isOpen || targetSelectedItem < 0) {
stackHomeRef.current.focus();
if (stackHomeRef.current) {
stackHomeRef.current.focus();
}
return;
}

const targetHomeHeight =
stackedCoachmarkRefs.current[targetSelectedItem].clientHeight;

stackHomeRef.current.style.height = `${targetHomeHeight}px`;
stackedCoachmarkRefs.current[targetSelectedItem].focus();
if (stackHomeRef.current) {
stackHomeRef.current.style.height = `${targetHomeHeight}px`;
stackedCoachmarkRefs.current[targetSelectedItem].focus();
}
}, [selectedItemNumber, isOpen, parentHeight]);

const wrappedChildren = Children.map(childArray, (child, idx) => {
Expand All @@ -189,7 +264,9 @@ export let CoachmarkStack = React.forwardRef(
return (
<CoachmarkOverlay
key={idx}
ref={(ref) => (stackedCoachmarkRefs.current[idx] = ref)}
ref={(ref) =>
(stackedCoachmarkRefs.current[idx] = ref as HTMLDivElement)
}
kind={COACHMARK_OVERLAY_KIND.STACKED}
onClose={() => handleClose(false)}
theme={theme}
Expand Down Expand Up @@ -225,7 +302,7 @@ export let CoachmarkStack = React.forwardRef(
<CoachmarkTagline title={tagline} onClose={onClose} />

<CoachmarkStackHome
ref={stackHomeRef}
ref={stackHomeRef as MutableRefObject<HTMLDivElement>}
className={cx(
`${pkg.prefix}--coachmark-overlay`,
`${pkg.prefix}--coachmark-overlay__${theme}`,
Expand All @@ -248,7 +325,7 @@ export let CoachmarkStack = React.forwardRef(
closeButtonLabel={closeButtonLabel}
title={title}
/>
{createPortal(wrappedChildren, portalNode)}
{createPortal(wrappedChildren, portalNode || document.body)}
</div>
</CoachmarkContext.Provider>
);
Expand Down Expand Up @@ -303,17 +380,17 @@ CoachmarkStack.propTypes = {
PropTypes.shape({
filePaths: PropTypes.arrayOf(PropTypes.string),
}),
]),
]) as PropTypes.Validator<Media>,

/**
* The labels used to link to the stackable Coachmarks.
*/
navLinkLabels: PropTypes.arrayOf(PropTypes.string).isRequired,
navLinkLabels: PropTypes.arrayOf(PropTypes.string.isRequired).isRequired,

/**
* Function to call when the CoachmarkStack closes.
*/
onClose: PropTypes.func,
onClose: PropTypes.func.isRequired,

/**
* Where in the DOM to render the stack.
Expand Down

0 comments on commit d41097a

Please sign in to comment.