Skip to content

Commit

Permalink
feat: Decouple lottie-web dependency from our repo (#6477)
Browse files Browse the repository at this point in the history
* feat: add renderMedia prop support

* feat: add deprecation notice

* feat: test case fix

* fix: update tests

* fix: update tests 2

* fix: prevent re-renders by memoizing

* fix: cspell

---------

Co-authored-by: Nandan Devadula <[email protected]>
  • Loading branch information
amal-k-joy and devadula-nandan authored Dec 4, 2024
1 parent 0da2372 commit 6b0a75b
Show file tree
Hide file tree
Showing 9 changed files with 168 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { CoachmarkOverlayElements } from '.';
import mdx from './CoachmarkOverlayElements.mdx';

import styles from './_storybook-styles.scss?inline';
import { SteppedAnimatedMedia } from '../SteppedAnimatedMedia';

export default {
title:
Expand All @@ -27,6 +28,7 @@ export default {
},
media: {
control: { type: null },
description: 'Deprecated: Property replaced by "renderMedia"',
},
},
parameters: {
Expand Down Expand Up @@ -68,5 +70,7 @@ coachmarkOverlayElements.args = {
nextButtonText: 'Next',
previousButtonLabel: 'Back',
className: 'myOverlayElements',
media: { filePaths: [Anim1, Anim2] },
renderMedia: ({ playStep }) => (
<SteppedAnimatedMedia filePaths={[Anim1, Anim2]} playStep={playStep} />
),
};
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,30 @@ describe(componentName, () => {
componentName
);
});
it(`renders an image with media prop`, async () => {
const user = userEvent.setup();
renderCoachmarkWithOverlayElements({
'data-testid': dataTestId,
media: { render: () => <img alt="img" /> },
});
const beaconOrButton = screen.getByRole('button', {
name: 'Show information',
});
await act(() => user.click(beaconOrButton));

expect(screen.getByRole('img')).toBeInTheDocument();
});
it(`renders an image`, async () => {
const user = userEvent.setup();
renderCoachmarkWithOverlayElements({
'data-testid': dataTestId,
renderMedia: () => <img alt="img" />,
});
const beaconOrButton = screen.getByRole('button', {
name: 'Show information',
});
await act(() => user.click(beaconOrButton));

expect(screen.getByRole('img')).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import React, {
ReactNode,
RefObject,
useEffect,
useMemo,
useRef,
useState,
} from 'react';
Expand Down Expand Up @@ -54,11 +55,16 @@ export interface CoachmarkOverlayElementsProps {
* 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}`.
* * @deprecated please use the `renderMedia` prop
*/
media?: {
render?: () => ReactNode;
filePaths?: string[];
};
/**
* Optional prop to render any media like images or any animated media.
*/
renderMedia?: (params) => ReactNode;
/**
* The label for the Next button.
*/
Expand Down Expand Up @@ -105,6 +111,7 @@ export let CoachmarkOverlayElements = React.forwardRef<
children,
isVisible = defaults.isVisible,
media,
renderMedia,
nextButtonText = defaults.nextButtonText,
previousButtonLabel = defaults.previousButtonLabel,
closeButtonLabel = defaults.closeButtonLabel,
Expand All @@ -118,6 +125,7 @@ export let CoachmarkOverlayElements = React.forwardRef<
const [scrollPosition, setScrollPosition] = useState(0);
const [currentProgStep, _setCurrentProgStep] = useState(0);
const coachmark = useCoachmark();
const hasMedia = media || renderMedia;

const setCurrentProgStep = (value) => {
if (currentProgStep > 0 && value === 0 && buttonFocusRef.current) {
Expand All @@ -132,6 +140,11 @@ export let CoachmarkOverlayElements = React.forwardRef<
const progStepFloor = 0;
const progStepCeil = numProgSteps - 1;

const renderMediaContent = useMemo(
() => renderMedia?.({ playStep: currentProgStep }),
[currentProgStep, renderMedia]
);

useEffect(() => {
// On mount, one of the two primary buttons ("next" or "close")
// will be rendered and must have focus. (a11y)
Expand Down Expand Up @@ -172,16 +185,19 @@ export let CoachmarkOverlayElements = React.forwardRef<
ref={ref}
{...getDevtoolsProps(componentName)}
>
{media &&
(media.render ? (
media.render()
) : (
<SteppedAnimatedMedia
className={`${blockClass}__element-stepped-media`}
filePaths={media.filePaths}
playStep={currentProgStep}
/>
))}
{hasMedia && media?.render && media.render()}
{hasMedia && media?.filePaths && (
<SteppedAnimatedMedia
className={`${blockClass}__element-stepped-media`}
filePaths={media.filePaths}
playStep={currentProgStep}
/>
)}
{hasMedia && renderMedia && (
<div className={`${blockClass}__element-stepped-media`}>
{renderMediaContent}
</div>
)}

{numProgSteps === 1 ? (
<>
Expand Down Expand Up @@ -313,6 +329,7 @@ CoachmarkOverlayElements.propTypes = {
* 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}`.
* @deprecated please use the `renderMedia` prop
*/
/**@ts-ignore*/
media: PropTypes.oneOfType([
Expand All @@ -331,4 +348,8 @@ CoachmarkOverlayElements.propTypes = {
* The label for the Previous button.
*/
previousButtonLabel: PropTypes.string,
/**
* Optional prop to render any media like images or animated media.
*/
renderMedia: PropTypes.func,
};
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export default {
},
media: {
control: { type: null },
description: 'Deprecated: Property replaced by "renderMedia"',
},
portalTarget: {
control: { type: null },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { CoachmarkHeader } from '../Coachmark/CoachmarkHeader';
import { SteppedAnimatedMedia } from '../SteppedAnimatedMedia';
import { useIsomorphicEffect } from '../../global/js/hooks';
import { ButtonProps } from '@carbon/react';
import { deprecateProp } from '../../global/js/utils/props-helper';

type Media =
| {
Expand Down Expand Up @@ -62,6 +63,10 @@ interface CoachmarkStackHomeProps {
* @see {@link MEDIA_PROP_TYPE}.
*/
media?: Media;
/**
* Optional prop to render any media like images or any animated media.
*/
renderMedia?: (params) => ReactNode;

/**
* The labels used to link to the stackable Coachmarks.
Expand Down Expand Up @@ -114,6 +119,7 @@ export let CoachmarkStackHome = forwardRef<
description,
isOpen,
media,
renderMedia,
navLinkLabels,
onClickNavItem,
onClose,
Expand All @@ -126,6 +132,9 @@ export let CoachmarkStackHome = forwardRef<
) => {
const buttonFocusRef = useRef<ButtonProps<React.ElementType> | null>(null);
const [linkFocusIndex, setLinkFocusIndex] = useState(0);

const hasMedia = media || renderMedia;

useEffect(() => {
setTimeout(() => {
if (isOpen && buttonFocusRef.current) {
Expand Down Expand Up @@ -190,20 +199,23 @@ export let CoachmarkStackHome = forwardRef<
/>
<div className={`${overlayClass}__body`}>
<div className={`${overlayClass}-element`}>
{!media && (
{!hasMedia && (
<Idea size={20} className={`${blockClass}__icon-idea`} />
)}

{media &&
(media.render ? (
media.render()
) : (
<SteppedAnimatedMedia
className={`${overlayClass}__element-stepped-media`}
filePaths={media.filePaths}
playStep={0}
/>
))}
{hasMedia && media?.render && media.render()}
{hasMedia && media?.filePaths && (
<SteppedAnimatedMedia
className={`${blockClass}__element-stepped-media`}
filePaths={media.filePaths}
playStep={0}
/>
)}
{hasMedia && renderMedia && (
<div className={`${blockClass}__element-stepped-media`}>
{renderMedia({ playStep: 0 })}
</div>
)}

<div className={`${overlayClass}-element__content`}>
{title && (
Expand Down Expand Up @@ -286,15 +298,20 @@ CoachmarkStackHome.propTypes = {
* If a stepped animation is required, use `{filePaths}`.
*
* @see {@link MEDIA_PROP_TYPE}.
* @deprecated please use the `renderMedia` prop
*/
media: PropTypes.oneOfType([
PropTypes.shape({
render: PropTypes.func,
}),
PropTypes.shape({
filePaths: PropTypes.arrayOf(PropTypes.string),
}),
]) as PropTypes.Validator<Media>,
media: deprecateProp(
PropTypes.oneOfType([
PropTypes.shape({
render: PropTypes.func,
}),
PropTypes.shape({
filePaths: PropTypes.arrayOf(PropTypes.string),
}),
]),
''
) as PropTypes.Validator<Media>,

/**
* The labels used to link to the stackable Coachmarks.
*/
Expand All @@ -318,6 +335,10 @@ CoachmarkStackHome.propTypes = {
* element is hidden or component is unmounted, the CoachmarkStackHome will disappear.
*/
portalTarget: PropTypes.string,
/**
* Optional prop to render any media like images or animated media.
*/
renderMedia: PropTypes.func,

/**
* The title of the Coachmark.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const InlineTipAnimation = new URL(
import.meta.url
).pathname;
import DocsPage from './InlineTip.docs-page';
import { SteppedAnimatedMedia } from '../SteppedAnimatedMedia';

export default {
title: 'Experimental/Onboarding/Inline tip/InlineTip',
Expand All @@ -36,13 +37,17 @@ export default {
options: ['None', '<InlineTipButton>', '<InlineTipLink>'],
control: { type: 'radio' },
},
media: {
renderMedia: {
options: ['None', 'Render a static image', 'Render an animation'],
control: { type: 'radio' },
},
narrow: {
control: { type: null },
},
media: {
control: { type: null },
description: 'Deprecated: Property replaced by "renderMedia"',
},
},
};

Expand All @@ -68,7 +73,7 @@ const defaultProps = {
collapsible: false,
action: 'None',
expandButtonLabel: 'Read more',
media: 'None',
renderMedia: 'None',
onClick: () => {
action(`Clicked the tertiary button`)();
},
Expand All @@ -80,16 +85,18 @@ const defaultProps = {
};

const Template = (args) => {
const { media, narrow, action: componentAction } = args;
const { renderMedia, narrow, action: componentAction } = args;

const selectedMedia = (function () {
switch (media) {
switch (renderMedia) {
case 'Render a static image':
return { render: () => <img alt="" src={InlineTipImage} /> };
return () => <img alt="" src={InlineTipImage} />;

case 'Render an animation':
return {
filePaths: [InlineTipAnimation],
};
return () => (
<SteppedAnimatedMedia filePaths={[InlineTipAnimation]} playStep={1} />
);

default:
return null;
}
Expand Down Expand Up @@ -129,7 +136,11 @@ const Template = (args) => {
narrow ? 'storybook--inline-tip-narrow' : 'storybook--inline-tip-wide',
])}
>
<InlineTip {...args} media={selectedMedia} action={selectedAction} />
<InlineTip
{...args}
renderMedia={selectedMedia}
action={selectedAction}
/>
</div>
);
};
Expand Down
13 changes: 12 additions & 1 deletion packages/ibm-products/src/components/InlineTip/InlineTip.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ describe(componentName, () => {
expect(screen.getByText(readLessLabel)).toBeInTheDocument();
});

it(`renders an image`, () => {
it(`renders an image with media prop`, () => {
render(
<InlineTip
title={title}
Expand All @@ -150,6 +150,17 @@ describe(componentName, () => {
);
expect(screen.getByRole('img')).toBeInTheDocument();
});
it(`renders an image`, () => {
render(
<InlineTip
title={title}
renderMedia={() => <img alt="img" src={InlineTipImage} />}
>
{children}
</InlineTip>
);
expect(screen.getByRole('img')).toBeInTheDocument();
});

it(`renders in the narrow format`, () => {
render(
Expand Down
Loading

0 comments on commit 6b0a75b

Please sign in to comment.