From fe3342229a9f7004ca0d289b91ad4d70c56ddf70 Mon Sep 17 00:00:00 2001 From: Matt Gallo Date: Thu, 11 Apr 2024 20:14:02 -0400 Subject: [PATCH] refactor: migrate Inlinetip and SteppedAnimatedMedia to ts --- .../InlineTip/{InlineTip.js => InlineTip.tsx} | 97 ++++++++++++++++++- ...matedMedia.js => SteppedAnimatedMedia.tsx} | 46 ++++++--- 2 files changed, 129 insertions(+), 14 deletions(-) rename packages/ibm-products/src/components/InlineTip/{InlineTip.js => InlineTip.tsx} (75%) rename packages/ibm-products/src/components/SteppedAnimatedMedia/{SteppedAnimatedMedia.js => SteppedAnimatedMedia.tsx} (76%) diff --git a/packages/ibm-products/src/components/InlineTip/InlineTip.js b/packages/ibm-products/src/components/InlineTip/InlineTip.tsx similarity index 75% rename from packages/ibm-products/src/components/InlineTip/InlineTip.js rename to packages/ibm-products/src/components/InlineTip/InlineTip.tsx index 1c07432e09..4348077f48 100644 --- a/packages/ibm-products/src/components/InlineTip/InlineTip.js +++ b/packages/ibm-products/src/components/InlineTip/InlineTip.tsx @@ -6,7 +6,15 @@ */ // Import portions of React that are needed. -import React, { useEffect, useMemo, useRef, useState } from 'react'; +import React, { + ForwardedRef, + PropsWithChildren, + ReactNode, + useEffect, + useMemo, + useRef, + useState, +} from 'react'; // Other standard imports. import { Close, Crossroads, Idea } from '@carbon/react/icons'; @@ -36,8 +44,90 @@ const defaults = { withLeftGutter: false, onClick: () => {}, onClose: () => {}, + title: 'Use case-specific heading', }; +type MediaType = { + render?: () => ReactNode; + filePaths?: string[]; +}; + +interface InlineTipProps { + /** + * Optional "call to action" ghost button or link that can appear + * directly below the content. This component comes with pre-styled + * elements available to use: `InlineTipLink` and `InlineTipButton`. + */ + action?: ReactNode; + /** + * Provide the contents of the InlineTip. + */ + children: ReactNode; + /** + * Provide an optional class to be applied to the containing node. + */ + className?: string; + /** + * Tooltip text and aria label for the Close button icon. + */ + closeIconDescription?: string; + /** + * The label for the collapse button. + * This button is not visible if `media` is specified. + */ + collapseButtonLabel?: string; + /** + * If set to `true`, it will truncate the body text to + * one line and expose an expand/collapse button toggle. + * + * This feature is disabled if `media` is specified. + */ + collapsible?: boolean; + /** + * The label for the expand button. + * This button is not visible if `media` is specified. + */ + expandButtonLabel?: string; + /** + * 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}`. + * + * Enabling `media` disables the `collapsible` feature. + */ + media: MediaType; + /** + * 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. + */ + onClick?: () => void; + /** + * Function to call when the InlineTip is closed via the "X" button. + */ + onClose?: () => void; + /** + * 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. + */ + tertiaryButtonLabel?: string; + /** + * The title of the InlineTip. + */ + title: string; + /** + * If true, insert 1 rem of "space" on the left of the component. + * This will allow the component's content to line up with other + * content on the page under special circumstances. + * + * This will only be applied when `narrow` is false. + */ + withLeftGutter?: boolean; +} + /** * Inline tips are messages embedded within other components that * provide an ambient way to deliver learning content without @@ -61,8 +151,8 @@ export let InlineTip = React.forwardRef( title = defaults.title, withLeftGutter = defaults.withLeftGutter, ...rest - }, - ref + }: PropsWithChildren, + ref: ForwardedRef ) => { const [isCollapsed, setIsCollapsed] = useState(collapsible); const labelId = useRef(uuidv4()).current; @@ -221,6 +311,7 @@ InlineTip.propTypes = { * * Enabling `media` disables the `collapsible` feature. */ + /**@ts-ignore*/ media: PropTypes.oneOfType([ PropTypes.shape({ render: PropTypes.func, diff --git a/packages/ibm-products/src/components/SteppedAnimatedMedia/SteppedAnimatedMedia.js b/packages/ibm-products/src/components/SteppedAnimatedMedia/SteppedAnimatedMedia.tsx similarity index 76% rename from packages/ibm-products/src/components/SteppedAnimatedMedia/SteppedAnimatedMedia.js rename to packages/ibm-products/src/components/SteppedAnimatedMedia/SteppedAnimatedMedia.tsx index 660c60c2b8..804d60b058 100644 --- a/packages/ibm-products/src/components/SteppedAnimatedMedia/SteppedAnimatedMedia.js +++ b/packages/ibm-products/src/components/SteppedAnimatedMedia/SteppedAnimatedMedia.tsx @@ -1,13 +1,19 @@ /** - * Copyright IBM Corp. 2023, 2023 + * Copyright IBM Corp. 2023, 2024 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ // Import portions of React that are needed. -import React, { useEffect, useRef, useState } from 'react'; -import lottie from 'lottie-web'; +import React, { + ForwardedRef, + useEffect, + useRef, + useState, + MutableRefObject, +} from 'react'; +import lottie, { type AnimationItem } from 'lottie-web'; import clamp from 'lodash/clamp'; // Other standard imports. import PropTypes from 'prop-types'; @@ -27,6 +33,21 @@ const defaults = { playStep: 0, }; +interface SteppedAnimatedMediaProps { + /** + * Optional class name for this component. + */ + className?: string; + /** + * The file path(s) to json formatted Lottie animation files. + */ + filePaths: string[] | undefined; + /** + * Which animation step from the filePaths array to play. + */ + playStep?: number; +} + /** * The SteppedAnimatedMedia is a Novice to Pro internal component and is not intended for general use. */ @@ -38,13 +59,15 @@ export const SteppedAnimatedMedia = React.forwardRef( playStep = defaults.playStep, filePaths, ...rest - }, - ref + }: SteppedAnimatedMediaProps, + ref: ForwardedRef ) => { - const [jsonData, setJsonData] = useState([]); - const animRef = useRef(); - const backupRef = useRef(); + const [jsonData, setJsonData] = useState([]); + const animRef = useRef(); + const backupRef = useRef(null); const localRef = ref ?? backupRef; + const localRefValue = (localRef as MutableRefObject) + .current; // load animation source useEffect(() => { const isJsonFile = (filePath) => filePath.includes('.json'); @@ -64,10 +87,10 @@ export const SteppedAnimatedMedia = React.forwardRef( const prefersReducedMotion = window?.matchMedia ? window.matchMedia('(prefers-reduced-motion: reduce)').matches : true; - if (localRef.current) { + if (localRefValue) { animRef.current?.destroy(); animRef.current = lottie.loadAnimation({ - container: localRef.current, + container: localRefValue, renderer: 'svg', animationData: jsonData[clamp(playStep, 0, jsonData.length - 1)], loop: false, @@ -82,7 +105,7 @@ export const SteppedAnimatedMedia = React.forwardRef( } return () => animRef.current?.destroy(); - }, [jsonData, localRef, playStep]); + }, [jsonData, localRefValue, playStep]); if (!jsonData) { return null; @@ -110,6 +133,7 @@ SteppedAnimatedMedia.propTypes = { /** * The file path(s) to json formatted Lottie animation files. */ + /**@ts-ignore*/ filePaths: PropTypes.arrayOf(PropTypes.string).isRequired, /** * Which animation step from the filePaths array to play.