diff --git a/packages/ibm-products/src/components/CoachmarkOverlayElements/CoachmarkOverlayElements.stories.jsx b/packages/ibm-products/src/components/CoachmarkOverlayElements/CoachmarkOverlayElements.stories.jsx
index 3c57f0468b..8e7d2ddcf1 100644
--- a/packages/ibm-products/src/components/CoachmarkOverlayElements/CoachmarkOverlayElements.stories.jsx
+++ b/packages/ibm-products/src/components/CoachmarkOverlayElements/CoachmarkOverlayElements.stories.jsx
@@ -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:
@@ -27,6 +28,7 @@ export default {
},
media: {
control: { type: null },
+ description: 'Deprecated: Property replaced by "renderMedia"',
},
},
parameters: {
@@ -68,5 +70,7 @@ coachmarkOverlayElements.args = {
nextButtonText: 'Next',
previousButtonLabel: 'Back',
className: 'myOverlayElements',
- media: { filePaths: [Anim1, Anim2] },
+ renderMedia: ({ playStep }) => (
+
+ ),
};
diff --git a/packages/ibm-products/src/components/CoachmarkOverlayElements/CoachmarkOverlayElements.test.js b/packages/ibm-products/src/components/CoachmarkOverlayElements/CoachmarkOverlayElements.test.js
index 0dcbac94e5..d9cf59f68f 100644
--- a/packages/ibm-products/src/components/CoachmarkOverlayElements/CoachmarkOverlayElements.test.js
+++ b/packages/ibm-products/src/components/CoachmarkOverlayElements/CoachmarkOverlayElements.test.js
@@ -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: () => },
+ });
+ 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: () => ,
+ });
+ const beaconOrButton = screen.getByRole('button', {
+ name: 'Show information',
+ });
+ await act(() => user.click(beaconOrButton));
+
+ expect(screen.getByRole('img')).toBeInTheDocument();
+ });
});
diff --git a/packages/ibm-products/src/components/CoachmarkOverlayElements/CoachmarkOverlayElements.tsx b/packages/ibm-products/src/components/CoachmarkOverlayElements/CoachmarkOverlayElements.tsx
index 47d07b0610..2db4b39812 100644
--- a/packages/ibm-products/src/components/CoachmarkOverlayElements/CoachmarkOverlayElements.tsx
+++ b/packages/ibm-products/src/components/CoachmarkOverlayElements/CoachmarkOverlayElements.tsx
@@ -13,6 +13,7 @@ import React, {
ReactNode,
RefObject,
useEffect,
+ useMemo,
useRef,
useState,
} from 'react';
@@ -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.
*/
@@ -105,6 +111,7 @@ export let CoachmarkOverlayElements = React.forwardRef<
children,
isVisible = defaults.isVisible,
media,
+ renderMedia,
nextButtonText = defaults.nextButtonText,
previousButtonLabel = defaults.previousButtonLabel,
closeButtonLabel = defaults.closeButtonLabel,
@@ -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) {
@@ -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)
@@ -172,16 +185,19 @@ export let CoachmarkOverlayElements = React.forwardRef<
ref={ref}
{...getDevtoolsProps(componentName)}
>
- {media &&
- (media.render ? (
- media.render()
- ) : (
-
- ))}
+ {hasMedia && media?.render && media.render()}
+ {hasMedia && media?.filePaths && (
+
+ )}
+ {hasMedia && renderMedia && (
+
+ {renderMediaContent}
+
+ )}
{numProgSteps === 1 ? (
<>
@@ -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([
@@ -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,
};
diff --git a/packages/ibm-products/src/components/CoachmarkStack/CoachmarkStack.stories.jsx b/packages/ibm-products/src/components/CoachmarkStack/CoachmarkStack.stories.jsx
index 2cb4168da4..53d395ec81 100644
--- a/packages/ibm-products/src/components/CoachmarkStack/CoachmarkStack.stories.jsx
+++ b/packages/ibm-products/src/components/CoachmarkStack/CoachmarkStack.stories.jsx
@@ -29,6 +29,7 @@ export default {
},
media: {
control: { type: null },
+ description: 'Deprecated: Property replaced by "renderMedia"',
},
portalTarget: {
control: { type: null },
diff --git a/packages/ibm-products/src/components/CoachmarkStack/CoachmarkStackHome.tsx b/packages/ibm-products/src/components/CoachmarkStack/CoachmarkStackHome.tsx
index 67cda9ddf7..6f69e96ae6 100644
--- a/packages/ibm-products/src/components/CoachmarkStack/CoachmarkStackHome.tsx
+++ b/packages/ibm-products/src/components/CoachmarkStack/CoachmarkStackHome.tsx
@@ -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 =
| {
@@ -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.
@@ -114,6 +119,7 @@ export let CoachmarkStackHome = forwardRef<
description,
isOpen,
media,
+ renderMedia,
navLinkLabels,
onClickNavItem,
onClose,
@@ -126,6 +132,9 @@ export let CoachmarkStackHome = forwardRef<
) => {
const buttonFocusRef = useRef | null>(null);
const [linkFocusIndex, setLinkFocusIndex] = useState(0);
+
+ const hasMedia = media || renderMedia;
+
useEffect(() => {
setTimeout(() => {
if (isOpen && buttonFocusRef.current) {
@@ -190,20 +199,23 @@ export let CoachmarkStackHome = forwardRef<
/>
- {!media && (
+ {!hasMedia && (
)}
- {media &&
- (media.render ? (
- media.render()
- ) : (
-
- ))}
+ {hasMedia && media?.render && media.render()}
+ {hasMedia && media?.filePaths && (
+
+ )}
+ {hasMedia && renderMedia && (
+
+ {renderMedia({ playStep: 0 })}
+
+ )}
{title && (
@@ -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: deprecateProp(
+ PropTypes.oneOfType([
+ PropTypes.shape({
+ render: PropTypes.func,
+ }),
+ PropTypes.shape({
+ filePaths: PropTypes.arrayOf(PropTypes.string),
+ }),
+ ]),
+ ''
+ ) as PropTypes.Validator,
+
/**
* The labels used to link to the stackable Coachmarks.
*/
@@ -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.
diff --git a/packages/ibm-products/src/components/InlineTip/InlineTip.stories.jsx b/packages/ibm-products/src/components/InlineTip/InlineTip.stories.jsx
index 038add24b1..8a42605c3d 100644
--- a/packages/ibm-products/src/components/InlineTip/InlineTip.stories.jsx
+++ b/packages/ibm-products/src/components/InlineTip/InlineTip.stories.jsx
@@ -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',
@@ -36,13 +37,17 @@ export default {
options: ['None', '', ''],
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"',
+ },
},
};
@@ -68,7 +73,7 @@ const defaultProps = {
collapsible: false,
action: 'None',
expandButtonLabel: 'Read more',
- media: 'None',
+ renderMedia: 'None',
onClick: () => {
action(`Clicked the tertiary button`)();
},
@@ -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: () => };
+ return () => ;
+
case 'Render an animation':
- return {
- filePaths: [InlineTipAnimation],
- };
+ return () => (
+
+ );
+
default:
return null;
}
@@ -129,7 +136,11 @@ const Template = (args) => {
narrow ? 'storybook--inline-tip-narrow' : 'storybook--inline-tip-wide',
])}
>
-
+
);
};
diff --git a/packages/ibm-products/src/components/InlineTip/InlineTip.test.js b/packages/ibm-products/src/components/InlineTip/InlineTip.test.js
index 5e68b785bf..102b43c692 100644
--- a/packages/ibm-products/src/components/InlineTip/InlineTip.test.js
+++ b/packages/ibm-products/src/components/InlineTip/InlineTip.test.js
@@ -139,7 +139,7 @@ describe(componentName, () => {
expect(screen.getByText(readLessLabel)).toBeInTheDocument();
});
- it(`renders an image`, () => {
+ it(`renders an image with media prop`, () => {
render(
{
);
expect(screen.getByRole('img')).toBeInTheDocument();
});
+ it(`renders an image`, () => {
+ render(
+ }
+ >
+ {children}
+
+ );
+ expect(screen.getByRole('img')).toBeInTheDocument();
+ });
it(`renders in the narrow format`, () => {
render(
diff --git a/packages/ibm-products/src/components/InlineTip/InlineTip.tsx b/packages/ibm-products/src/components/InlineTip/InlineTip.tsx
index 82ba2a2c06..fd85e9eedc 100644
--- a/packages/ibm-products/src/components/InlineTip/InlineTip.tsx
+++ b/packages/ibm-products/src/components/InlineTip/InlineTip.tsx
@@ -93,12 +93,18 @@ export interface InlineTipProps {
* - If a stepped animation is required, use `{filePaths}`.
*
* Enabling `media` disables the `collapsible` feature.
+ * @deprecated please use the `renderMedia` prop
*/
media?: MediaType;
+ /**
+ * Optional prop to render any media like images or any animated media.
+ */
+ renderMedia?: () => ReactNode;
/**
* Set to `true` to arrange the information in a format
* that is easier to read in a limited space.
*/
+
narrow?: boolean;
/**
* Function to call when the tertiary button is clicked.
@@ -143,6 +149,7 @@ export let InlineTip = React.forwardRef(
collapseButtonLabel = defaults.collapseButtonLabel,
expandButtonLabel = defaults.expandButtonLabel,
media,
+ renderMedia,
narrow = defaults.narrow,
onClick,
onClose,
@@ -153,6 +160,8 @@ export let InlineTip = React.forwardRef(
}: PropsWithChildren,
ref: ForwardedRef
) => {
+ const hasMedia = renderMedia || media;
+
const [isCollapsed, setIsCollapsed] = useState(collapsible);
const labelId = useRef(uuidv4()).current;
@@ -162,7 +171,7 @@ export let InlineTip = React.forwardRef(
);
let childrenToRender = children;
- if (!media && collapsible && isCollapsed) {
+ if (!hasMedia && collapsible && isCollapsed) {
childrenToRender = (
{previewText}
);
@@ -182,7 +191,7 @@ export let InlineTip = React.forwardRef(
className,
collapsible && `${blockClass}__collapsible`,
isCollapsed && `${blockClass}__collapsible-collapsed`,
- media && `${blockClass}__has-media`,
+ hasMedia && `${blockClass}__has-media`,
[narrow ? `${blockClass}__narrow` : `${blockClass}__wide`],
withLeftGutter && !narrow && `${blockClass}__with-left-gutter`
)}
@@ -203,7 +212,7 @@ export let InlineTip = React.forwardRef(
{/* Hide the idea icon if is narrow and showing an image */}
- {((!media && narrow) || !narrow) && (
+ {((!hasMedia && narrow) || !narrow) && (
@@ -224,7 +233,7 @@ export let InlineTip = React.forwardRef(
{(collapsible || tertiaryButtonLabel) && (
- {media &&
- (media.render ? (
- {media.render()}
- ) : (
-
- ))}
+ {hasMedia && media?.render && (
+ {media.render()}
+ )}
+ {hasMedia && media?.filePaths && (
+
+ )}
+ {hasMedia && renderMedia && (
+ {renderMedia()}
+ )}
);
}
@@ -309,6 +321,7 @@ InlineTip.propTypes = {
* - If a stepped animation is required, use `{filePaths}`.
*
* Enabling `media` disables the `collapsible` feature.
+ * @deprecated please use the `renderMedia` prop
*/
/**@ts-ignore*/
media: PropTypes.oneOfType([
@@ -332,6 +345,10 @@ InlineTip.propTypes = {
* Function to call when the InlineTip is closed via the "X" button.
*/
onClose: PropTypes.func,
+ /**
+ * Optional prop to render any media like images or animated media.
+ */
+ renderMedia: PropTypes.func,
/**
* Defining the label will show a the tertiary button with the crossroads icon.
* You will still need to define the `onClose` method to trigger a callback.
diff --git a/packages/ibm-products/src/components/SteppedAnimatedMedia/SteppedAnimatedMedia.tsx b/packages/ibm-products/src/components/SteppedAnimatedMedia/SteppedAnimatedMedia.tsx
index 57cadd0fe2..fec4de8517 100644
--- a/packages/ibm-products/src/components/SteppedAnimatedMedia/SteppedAnimatedMedia.tsx
+++ b/packages/ibm-products/src/components/SteppedAnimatedMedia/SteppedAnimatedMedia.tsx
@@ -68,6 +68,7 @@ export const SteppedAnimatedMedia = React.forwardRef(
const localRef = ref ?? backupRef;
const localRefValue = (localRef as MutableRefObject)
.current;
+ const filePathStr = filePaths?.join(); //converting the array to string will avoid unwanted useEffect trigger.
// load animation source
useEffect(() => {
const isJsonFile = (filePath) => filePath.includes('.json');
@@ -81,7 +82,8 @@ export const SteppedAnimatedMedia = React.forwardRef(
}
}
loadArtifact();
- }, [filePaths]);
+ // eslint-disable-next-line
+ }, [filePathStr]);
useEffect(() => {
const prefersReducedMotion = window?.matchMedia