From b3c3b29f30c5f9f7022b9432c2601dc98b157825 Mon Sep 17 00:00:00 2001 From: Ignacio Becerra Date: Sun, 9 Jun 2024 22:49:57 -0700 Subject: [PATCH] refactor(CreateFullPageStep): add TypeScript types (#5169) * refactor(CreateFullPageStep): add TypeScript types * Update packages/ibm-products/src/components/CreateFullPage/CreateFullPageStep.tsx Co-authored-by: Matt Gallo * fix(CreateFullStep): address feedback --------- Co-authored-by: David Menendez Co-authored-by: Matt Gallo --- .../CreateFullPage/CreateFullPage.tsx | 3 +- ...FullPageStep.js => CreateFullPageStep.tsx} | 95 ++++++++++++++++++- .../src/global/js/hooks/usePreviousValue.js | 5 +- .../global/js/hooks/useRetrieveStepData.js | 10 +- 4 files changed, 100 insertions(+), 13 deletions(-) rename packages/ibm-products/src/components/CreateFullPage/{CreateFullPageStep.js => CreateFullPageStep.tsx} (72%) diff --git a/packages/ibm-products/src/components/CreateFullPage/CreateFullPage.tsx b/packages/ibm-products/src/components/CreateFullPage/CreateFullPage.tsx index 8622038c15..78e81c5da9 100644 --- a/packages/ibm-products/src/components/CreateFullPage/CreateFullPage.tsx +++ b/packages/ibm-products/src/components/CreateFullPage/CreateFullPage.tsx @@ -45,6 +45,7 @@ import { SimpleHeader, overflowAriaLabel_required_if_breadcrumbs_exist, } from '../SimpleHeader/SimpleHeader'; +import { StepsContextType } from '../CreateTearsheet/CreateTearsheet'; const blockClass = `${pkg.prefix}--create-full-page`; const componentName = 'CreateFullPage'; @@ -52,7 +53,7 @@ const componentName = 'CreateFullPage'; // This is a general context for the steps container // containing information about the state of the container // and providing some callback methods for steps to use -export const StepsContext = createContext(null); +export const StepsContext = createContext(null); // This is a context supplied separately to each step in the container // to let it know what number it is in the sequence of steps diff --git a/packages/ibm-products/src/components/CreateFullPage/CreateFullPageStep.js b/packages/ibm-products/src/components/CreateFullPage/CreateFullPageStep.tsx similarity index 72% rename from packages/ibm-products/src/components/CreateFullPage/CreateFullPageStep.js rename to packages/ibm-products/src/components/CreateFullPage/CreateFullPageStep.tsx index f056b4412a..a406dee480 100644 --- a/packages/ibm-products/src/components/CreateFullPage/CreateFullPageStep.js +++ b/packages/ibm-products/src/components/CreateFullPage/CreateFullPageStep.tsx @@ -6,6 +6,9 @@ */ import React, { + ForwardedRef, + PropsWithChildren, + ReactNode, forwardRef, isValidElement, useContext, @@ -25,9 +28,88 @@ const blockClass = `${pkg.prefix}--create-full-page__step`; // Default values for props const defaults = { - includeStep: true, + includeStep: true as boolean, }; +interface CreateFullPageStepBaseProps extends PropsWithChildren { + /** + * Sets an optional className to be added to the CreateFullPage step + */ + className?: string; + + /** + * Sets an optional description on the progress step component + */ + description?: ReactNode; + + /** + * This will conditionally disable the submit button in the multi step CreateFullPage + */ + disableSubmit?: boolean; + + /** + * This optional prop will render your form content inside of a fieldset html element + */ + hasFieldset: boolean; + + /** + * This prop is used to help track dynamic steps. If this value is `false` then the step is not included in the visible steps or the ProgressIndicator + * steps. If this value is `true` then the step will be included in the list of visible steps, as well as being included in the ProgressIndicator step list + */ + includeStep?: boolean; + + /** + * This prop can be used on the first step to mark it as an intro step, which will not render the progress indicator steps + */ + introStep?: boolean; + + /** + * This optional prop will indicate an error icon on the progress indicator step item + */ + invalid?: boolean; + + /** + * Optional function to be called on initial mount of a step. + * For example, this can be used to fetch data that is required on a particular step. + */ + onMount?: () => void; + + /** + * Optional function to be called on a step change. + * For example, this can be used to validate input fields before proceeding to the next step. + * This function can _optionally_ return a promise that is either resolved or rejected and the CreateFullPage will handle the submitting state of the next button. + */ + onNext?: () => void | Promise; + + /** + * Sets the optional secondary label on the progress step component + */ + secondaryLabel?: string; + + /** + * Sets an optional subtitle on the progress step component + */ + subtitle?: string; + + /** + * Sets the title text for a create full page step + */ + title: ReactNode; +} + +type CreateFullPageStepFieldsetProps = + | { + hasFieldset: false; + fieldsetLegendText?: string; + } + | { + hasFieldset?: true; + fieldsetLegendText: string; + }; + +type CreateFullPageStepProps = CreateFullPageStepBaseProps & + CreateFullPageStepFieldsetProps; + export let CreateFullPageStep = forwardRef( ( { @@ -50,12 +132,12 @@ export let CreateFullPageStep = forwardRef( // Collect any other property values passed in. ...rest - }, - ref + }: CreateFullPageStepProps, + ref: ForwardedRef ) => { const stepsContext = useContext(StepsContext); const stepNumber = useContext(StepNumberContext); - const [shouldIncludeStep, setShouldIncludeStep] = useState(); + const [shouldIncludeStep, setShouldIncludeStep] = useState(); const previousState = usePreviousValue({ currentStep: stepsContext?.currentStep, }); @@ -89,7 +171,7 @@ export let CreateFullPageStep = forwardRef( // steps container context so that it can manage the 'Next' button appropriately. useEffect(() => { if (stepNumber === stepsContext?.currentStep) { - stepsContext.setIsDisabled(disableSubmit); + stepsContext.setIsDisabled(disableSubmit as boolean); stepsContext?.setOnNext(onNext); // needs to be updated here otherwise there could be stale state values from only initially setting onNext } }, [stepsContext, stepNumber, disableSubmit, onNext]); @@ -189,11 +271,13 @@ CreateFullPageStep.propTypes = { /** * This will conditionally disable the submit button in the multi step CreateFullPage */ + /**@ts-ignore */ disableSubmit: PropTypes.bool, /** * This is the legend text that appears above a fieldset html element for accessibility purposes. It is required when the optional `hasFieldset` prop is provided to a FullPageStep. */ + /**@ts-ignore */ fieldsetLegendText: PropTypes.string.isRequired.if( ({ hasFieldset }) => hasFieldset === true ), @@ -201,6 +285,7 @@ CreateFullPageStep.propTypes = { /** * This optional prop will render your form content inside of a fieldset html element */ + /**@ts-ignore */ hasFieldset: PropTypes.bool, /** diff --git a/packages/ibm-products/src/global/js/hooks/usePreviousValue.js b/packages/ibm-products/src/global/js/hooks/usePreviousValue.js index 74ce23aa27..9ff08d60d2 100644 --- a/packages/ibm-products/src/global/js/hooks/usePreviousValue.js +++ b/packages/ibm-products/src/global/js/hooks/usePreviousValue.js @@ -8,8 +8,9 @@ import { useEffect, useRef } from 'react'; /** - * Returns the previous state values included in the param - * @param {object} value + * Returns the previous state value included in the param + * @param {object} value - The current value of any type. + * @returns {T | undefined} - The previous value of the same type, or undefined if there is none */ export const usePreviousValue = (value) => { const ref = useRef(); diff --git a/packages/ibm-products/src/global/js/hooks/useRetrieveStepData.js b/packages/ibm-products/src/global/js/hooks/useRetrieveStepData.js index 58764d4d5a..efa03b8a7d 100644 --- a/packages/ibm-products/src/global/js/hooks/useRetrieveStepData.js +++ b/packages/ibm-products/src/global/js/hooks/useRetrieveStepData.js @@ -13,11 +13,11 @@ import { useEffect } from 'react'; * @param {object} useResetCreateComponent * @param {object} useResetCreateComponent.stepsContext * @param {number} useResetCreateComponent.stepNumber - * @param {boolean} useResetCreateComponent.introStep - * @param {boolean} useResetCreateComponent.invalid - * @param {boolean} useResetCreateComponent.shouldIncludeStep - * @param {string} useResetCreateComponent.secondaryLabel - * @param {React.ReactNode} useResetCreateComponent.title + * @param {boolean | undefined} useResetCreateComponent.introStep + * @param {boolean | undefined} useResetCreateComponent.invalid + * @param {boolean | undefined} useResetCreateComponent.shouldIncludeStep + * @param {string | undefined } useResetCreateComponent.secondaryLabel + * @param {string | React.ReactNode} useResetCreateComponent.title */ export const useRetrieveStepData = ({ stepsContext,