Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: migrate Inlinetip and SteppedAnimatedMedia to TypeScript #4877

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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
Expand All @@ -61,8 +151,8 @@ export let InlineTip = React.forwardRef(
title = defaults.title,
withLeftGutter = defaults.withLeftGutter,
...rest
},
ref
}: PropsWithChildren<InlineTipProps>,
ref: ForwardedRef<HTMLDivElement>
) => {
const [isCollapsed, setIsCollapsed] = useState(collapsible);
const labelId = useRef(uuidv4()).current;
Expand Down Expand Up @@ -221,6 +311,7 @@ InlineTip.propTypes = {
*
* Enabling `media` disables the `collapsible` feature.
*/
/**@ts-ignore*/
media: PropTypes.oneOfType([
PropTypes.shape({
render: PropTypes.func,
Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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.
*/
Expand All @@ -38,13 +59,15 @@ export const SteppedAnimatedMedia = React.forwardRef(
playStep = defaults.playStep,
filePaths,
...rest
},
ref
}: SteppedAnimatedMediaProps,
ref: ForwardedRef<HTMLDivElement>
) => {
const [jsonData, setJsonData] = useState([]);
const animRef = useRef();
const backupRef = useRef();
const [jsonData, setJsonData] = useState<object[]>([]);
const animRef = useRef<AnimationItem>();
const backupRef = useRef<HTMLDivElement>(null);
const localRef = ref ?? backupRef;
const localRefValue = (localRef as MutableRefObject<HTMLDivElement>)
.current;
// load animation source
useEffect(() => {
const isJsonFile = (filePath) => filePath.includes('.json');
Expand All @@ -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,
Expand All @@ -82,7 +105,7 @@ export const SteppedAnimatedMedia = React.forwardRef(
}

return () => animRef.current?.destroy();
}, [jsonData, localRef, playStep]);
}, [jsonData, localRefValue, playStep]);

if (!jsonData) {
return null;
Expand Down Expand Up @@ -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.
Expand Down
Loading