From e44bc147b707b30a03b468db555e37d4df978e3b Mon Sep 17 00:00:00 2001
From: Branden Brown <29528409+20BBrown14@users.noreply.github.com>
Date: Tue, 1 Feb 2022 15:44:10 -0600
Subject: [PATCH] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20Migrate=20SQFormGuid?=
=?UTF-8?q?edWorkflow=20to=20TS?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
BREAKING CHANGE: ๐งจ SQFormGuidedWorkflow prop 'actionButton' renamed to 'actions'. Removed
props 'isPanelExpanded' and 'expandPanel'
โ
Closes: #567
---
README.md | 38 ++++
...on.js => AdditionalInformationSection.tsx} | 10 +-
.../{AgentScript.js => AgentScript.tsx} | 10 +-
.../{Header.js => Header.tsx} | 12 +-
.../{OutcomeForm.js => OutcomeForm.tsx} | 10 +-
.../SQFormGuidedWorkflow/PropTypes.js | 126 -------------
src/components/SQFormGuidedWorkflow/README.md | 3 +
...edWorkflow.js => SQFormGuidedWorkflow.tsx} | 57 +++---
src/components/SQFormGuidedWorkflow/Types.ts | 165 ++++++++++++++++++
.../{index.js => index.ts} | 0
.../useGuidedWorkflowContext.js | 44 -----
.../useGuidedWorkflowContext.ts | 61 +++++++
...TaskModules.js => useManageTaskModules.ts} | 55 ++++--
stories/SQFormGuidedWorkflow.stories.js | 2 +-
.../SQFormGuidedWorkflow.stories.test.js | 18 +-
15 files changed, 374 insertions(+), 237 deletions(-)
rename src/components/SQFormGuidedWorkflow/{AdditionalInformationSection.js => AdditionalInformationSection.tsx} (75%)
rename src/components/SQFormGuidedWorkflow/{AgentScript.js => AgentScript.tsx} (79%)
rename src/components/SQFormGuidedWorkflow/{Header.js => Header.tsx} (89%)
rename src/components/SQFormGuidedWorkflow/{OutcomeForm.js => OutcomeForm.tsx} (83%)
delete mode 100644 src/components/SQFormGuidedWorkflow/PropTypes.js
rename src/components/SQFormGuidedWorkflow/{SQFormGuidedWorkflow.js => SQFormGuidedWorkflow.tsx} (78%)
create mode 100644 src/components/SQFormGuidedWorkflow/Types.ts
rename src/components/SQFormGuidedWorkflow/{index.js => index.ts} (100%)
delete mode 100644 src/components/SQFormGuidedWorkflow/useGuidedWorkflowContext.js
create mode 100644 src/components/SQFormGuidedWorkflow/useGuidedWorkflowContext.ts
rename src/components/SQFormGuidedWorkflow/{useManageTaskModules.js => useManageTaskModules.ts} (72%)
diff --git a/README.md b/README.md
index ae65d427..96c99f13 100644
--- a/README.md
+++ b/README.md
@@ -40,6 +40,44 @@ For **BREAKING CHANGES** Type a brief description of the breaking change when as
## Breaking Changes
### Version `[Typescript Version]`
+
+#### SQFormGuidedWorkflow changes
+ - Removed TaskModule properties: `isPanelExpanded` and `expandPanel`
+
+
+ - In SQForm v`[Typescript Version]` `actionButton` was renamed to `actions` as part of the taskModule definitions. Functionality remains the same.
+
+```jsx
+// โ๏ธ Example
+const taskModules = [
+ { ... },
+ { ... },
+ {
+ ...
+ scriptedTextProps: {
+ text: `Stuff about policy cancellation documents`,
+ title: 'Agent Script',
+ actionButton: View Doc, // This prop was renamed
+ },
+ }
+]
+
+// โ
Example
+const taskModules = [
+ { ... },
+ { ... },
+ {
+ ...
+ scriptedTextProps: {
+ text: `Stuff about policy cancellation documents`,
+ title: 'Agent Script',
+ actions: View Doc, // This prop was renamed
+ },
+ }
+]
+```
+
+#### SQForm no longer allows `boolean`s as dropdown options
In SQForm v`[Typescript Version]` support for `boolean` valued dropdown options was removed. Material-UI and HTML Select components do not support options with `boolean`s as values and causes type conflicts with our own library. Therefore, if you're upgrading this version and you using `boolean` values options you'll need to take care to update those. Below is our recommended changes.
```js
diff --git a/src/components/SQFormGuidedWorkflow/AdditionalInformationSection.js b/src/components/SQFormGuidedWorkflow/AdditionalInformationSection.tsx
similarity index 75%
rename from src/components/SQFormGuidedWorkflow/AdditionalInformationSection.js
rename to src/components/SQFormGuidedWorkflow/AdditionalInformationSection.tsx
index 47bb7ec5..32d374a8 100644
--- a/src/components/SQFormGuidedWorkflow/AdditionalInformationSection.js
+++ b/src/components/SQFormGuidedWorkflow/AdditionalInformationSection.tsx
@@ -1,10 +1,10 @@
import React from 'react';
import {Section, SectionBody} from 'scplus-shared-components';
-import {AdditionalInformationPropTypes} from './PropTypes';
import Header from './Header';
+import type {AdditionalInformationProps} from './Types';
function AdditionalInformationSection({
- actionButton,
+ actions,
title,
infoText,
warningText,
@@ -12,11 +12,11 @@ function AdditionalInformationSection({
successText,
isFailedState,
Elements,
-}) {
+}: AdditionalInformationProps): React.ReactElement {
return (
{
- if (!shouldRenderInformativeHeading) {
+ if (!shouldRenderInformativeHeading || !informativeHeadingType) {
return;
}
@@ -66,11 +66,9 @@ function Header({
informativeHeading={getInformativeHeadingText()}
type={informativeHeadingType}
>
- {actionButton}
+ {actions}
);
}
-Header.propTypes = HeaderPropTypes;
-
export default Header;
diff --git a/src/components/SQFormGuidedWorkflow/OutcomeForm.js b/src/components/SQFormGuidedWorkflow/OutcomeForm.tsx
similarity index 83%
rename from src/components/SQFormGuidedWorkflow/OutcomeForm.js
rename to src/components/SQFormGuidedWorkflow/OutcomeForm.tsx
index 30557916..347bae09 100644
--- a/src/components/SQFormGuidedWorkflow/OutcomeForm.js
+++ b/src/components/SQFormGuidedWorkflow/OutcomeForm.tsx
@@ -2,10 +2,10 @@ import React from 'react';
import {Grid} from '@material-ui/core';
import {Section, SectionBody} from 'scplus-shared-components';
import Header from './Header';
-import {OutcomePropTypes} from './PropTypes';
+import type {OutcomeProps} from './Types';
function OutcomeForm({
- actionButton,
+ actions,
FormElements,
title,
infoText,
@@ -14,11 +14,11 @@ function OutcomeForm({
successText,
isFailedState,
muiGridProps = {},
-}) {
+}: OutcomeProps): React.ReactElement {
return (
component */
- muiGridProps: PropTypes.object,
- ...HeaderPropTypes,
-};
-
-export const FormikProps = {
- /** Form Entity Object aka initial values of the form */
- initialValues: PropTypes.object.isRequired,
- /** Form Submission Handler | @typedef onSubmit: (values: Values, formikBag: FormikBag, context) => void | Promise */
- onSubmit: PropTypes.func.isRequired,
- /** Yup validation schema shape */
- validationSchema: PropTypes.object,
-};
-
-export const TaskModuleProps = {
- /** Unique name used as a key for managing expansion state within Accordion */
- name: PropTypes.string.isRequired,
- /** Title text */
- title: PropTypes.string.isRequired,
- /** Subtitle text - Each Subtitle is separated by a pipe "|" */
- subtitles: PropTypes.arrayOf(PropTypes.string),
- /** Panel is disabled, the user cannot toggle the panel while disabled */
- isDisabled: PropTypes.bool,
- /** Is initially expanded */
- isInitiallyExpanded: PropTypes.bool,
- /** Controlled loading */
- isLoading: PropTypes.bool,
- /** Custom loading message for controlled loading */
- isLoadingMessage: PropTypes.string,
- /** expandPanel callback synchronizes consumer state with Accordion state.
- * Requires isPanelExpanded prop. */
- expandPanel: PropTypes.func,
- /** Optional prop for the consumer to define the cards open/close state.
- * Requires expandPanel prop. */
- isPanelExpanded: PropTypes.bool,
- /** Optional click handler if you want to perform a side effect on click */
- onClick: PropTypes.func,
- /** Reset button text */
- resetButtonText: PropTypes.string,
- /** Submit button text */
- submitButtonText: PropTypes.string,
- /** Flag for if the form is in a failed state where the user cannot continue */
- isFailedState: PropTypes.bool,
- /** Manually disable submit button */
- isSubmitButtonDisabled: PropTypes.bool,
- /** The props used to configure SQForm */
- formikProps: PropTypes.shape(FormikProps).isRequired,
- /** The props used to configure the Additional Information section */
- additionalInformationSectionProps: PropTypes.shape(
- AdditionalInformationPropTypes
- ),
- /** The props used to configure the Scripted Text section */
- scriptedTextProps: PropTypes.shape(AgentScriptPropTypes).isRequired,
- /** The props used to configure the Outcome form section */
- outcomeProps: PropTypes.shape(OutcomePropTypes).isRequired,
-};
-
-export const GuidedWorkflowProps = {
- /** Main Title */
- mainTitle: PropTypes.string.isRequired,
- /** Main Subtitle Informative Text */
- mainSubtitle: PropTypes.string,
- /** Number of tasks completed (Default is zero) */
- initialCompletedTasks: PropTypes.number,
- /**
- * Disables all Task Modules except the current Active Task module
- * This prevents the user from returning to a previous task module
- */
- isStrictMode: PropTypes.bool,
- /**
- * Callback function which passes the error as an argument for the consumer to handle
- * Usually the consumer will render an alert to signal an error occured
- */
- onError: PropTypes.func.isRequired,
- /** Task Module configuration Object(s) */
- taskModules: PropTypes.arrayOf(PropTypes.shape(TaskModuleProps).isRequired)
- .isRequired,
- /** An object of css-in-js style properties to be passed */
- containerStyles: PropTypes.object,
-};
diff --git a/src/components/SQFormGuidedWorkflow/README.md b/src/components/SQFormGuidedWorkflow/README.md
index 3d2e4718..c7e1f79d 100644
--- a/src/components/SQFormGuidedWorkflow/README.md
+++ b/src/components/SQFormGuidedWorkflow/README.md
@@ -14,6 +14,9 @@ This section covers how to properly setup the configuration object array for the
- [The Task Module Props](https://www.loom.com/share/ca6de9ff9cfc4faba4d500e8c244c5d9)
> After recording this video I made a code change that added another prop to the task modules props, `isFailedState` -[The `isFailedState` Task Module Prop](https://www.loom.com/share/5a636e9ca7884bcf9697b00ba7f09da2)
+ > Changes were made that removed the following props: `isPanelExpanded` and `expandPanel`. See the Breaking Changes section of the project README for more information.
+ > The `actionButton` prop was renamed to `actions`. Functionality remains the same. See the Breaking Changes section of the project README for more information.
+
### Scripted Text Section
diff --git a/src/components/SQFormGuidedWorkflow/SQFormGuidedWorkflow.js b/src/components/SQFormGuidedWorkflow/SQFormGuidedWorkflow.tsx
similarity index 78%
rename from src/components/SQFormGuidedWorkflow/SQFormGuidedWorkflow.js
rename to src/components/SQFormGuidedWorkflow/SQFormGuidedWorkflow.tsx
index 92b43d67..a7a8909a 100644
--- a/src/components/SQFormGuidedWorkflow/SQFormGuidedWorkflow.js
+++ b/src/components/SQFormGuidedWorkflow/SQFormGuidedWorkflow.tsx
@@ -1,6 +1,7 @@
import React from 'react';
import * as Yup from 'yup';
import {Formik, Form} from 'formik';
+import type {FormikHelpers} from 'formik';
import {CardActions, CardContent, makeStyles} from '@material-ui/core';
import {
Accordion,
@@ -15,7 +16,7 @@ import OutcomeForm from './OutcomeForm';
import AdditionalInformationSection from './AdditionalInformationSection';
import {useManageTaskModules} from './useManageTaskModules';
import {useGuidedWorkflowContext} from './useGuidedWorkflowContext';
-import {GuidedWorkflowProps} from './PropTypes';
+import type {SQFormDataProps, SQFormGuidedWorkflowProps} from './Types';
const useStyles = makeStyles(() => {
return {
@@ -35,18 +36,7 @@ const getTaskModuleFormSchema = (validationSchema = {}) => {
return Yup.object().shape(validationSchema);
};
-// Until Formik exposes the validationSchema (again) via Context, the solution has to be handled at the Form declaration level
-// There's a few open PR's on this issue, here's one for reference: https://github.com/formium/formik/pull/2933
-const getFormikInitialRequiredErrors = (validationSchema = {}) => {
- Object.entries(validationSchema).reduce((acc, [key, value]) => {
- if (value._exclusive?.required) {
- return {...acc, [key]: 'Required'};
- }
- return acc;
- }, {});
-};
-
-function SQFormGuidedWorkflow({
+function SQFormGuidedWorkflow({
taskModules,
mainTitle,
mainSubtitle,
@@ -54,14 +44,33 @@ function SQFormGuidedWorkflow({
isStrictMode = false,
onError,
containerStyles = {},
-}) {
+}: SQFormGuidedWorkflowProps): React.ReactElement {
+ // Until Formik exposes the validationSchema (again) via Context, the solution has to be handled at the Form declaration level
+ // There's a few open PR's on this issue, here's one for reference: https://github.com/formium/formik/pull/2933
+ const getFormikInitialRequiredErrors = (
+ validationSchema?: SQFormDataProps['validationSchema']
+ ) => {
+ if (validationSchema) {
+ return Object.entries(validationSchema).reduce((acc, [key, value]) => {
+ if (value.tests[0]?.OPTIONS.name === 'required') {
+ return {...acc, [key]: 'Required'};
+ }
+ return acc;
+ }, {});
+ }
+
+ return {};
+ };
+
const classes = useStyles();
- const [taskModulesContext, updateTaskModuleContextByID] =
- useGuidedWorkflowContext(taskModules);
+ const {
+ state: taskModulesContext,
+ updateDataByID: updateTaskModuleContextByID,
+ } = useGuidedWorkflowContext(taskModules);
const {taskModulesState, updateActiveTaskModule, enableNextTaskModule} =
- useManageTaskModules(initialCompletedTasks, taskModulesContext);
+ useManageTaskModules(initialCompletedTasks, taskModulesContext);
const transformedTaskModules = taskModules.map((taskModule, index) => {
const taskNumber = index + 1;
@@ -88,7 +97,10 @@ function SQFormGuidedWorkflow({
return false;
};
- const handleSubmit = async (values, formikBag) => {
+ const handleSubmit = async (
+ values: TValues,
+ formikBag: FormikHelpers
+ ) => {
const context = {
...taskModulesContext,
[taskNumber]: {
@@ -102,7 +114,7 @@ function SQFormGuidedWorkflow({
updateTaskModuleContextByID(taskNumber, values);
enableNextTaskModule();
} catch (error) {
- onError(error);
+ onError && onError(error);
}
};
@@ -110,9 +122,12 @@ function SQFormGuidedWorkflow({
...taskModule,
isDisabled: getIsDisabled(),
isInitiallyExpanded: taskModule.isInitiallyExpanded || isPanelExpanded,
- expandPanel: () => {}, // Faulty logic in the Accordion SSC requires precense of a function for isPanelExpanded to work
+ expandPanel: () => {
+ /* do nothing */
+ }, // Faulty logic in the Accordion SSC requires precense of a function for isPanelExpanded to work
isPanelExpanded,
onClick: () => {
+ taskModule?.onClick && taskModule.onClick();
updateActiveTaskModule(taskNumber);
},
body: (
@@ -188,6 +203,4 @@ function SQFormGuidedWorkflow({
);
}
-SQFormGuidedWorkflow.propTypes = GuidedWorkflowProps;
-
export default SQFormGuidedWorkflow;
diff --git a/src/components/SQFormGuidedWorkflow/Types.ts b/src/components/SQFormGuidedWorkflow/Types.ts
new file mode 100644
index 00000000..50ec9c52
--- /dev/null
+++ b/src/components/SQFormGuidedWorkflow/Types.ts
@@ -0,0 +1,165 @@
+import React from 'react';
+import type {GridProps} from '@material-ui/core';
+import type {FormikHelpers} from 'formik';
+import type {AnySchema} from 'yup';
+
+export interface HeaderProps {
+ /** Title to display in the section header */
+ title: string;
+ /** Optional elements to display in the section header */
+ actions?: React.ReactElement;
+ /** Informative text to display as a subheader next to the title */
+ infoText?: string;
+ /** Warning text to display as a subheader next to the title */
+ warningText?: string;
+ /** Error text to display as a subheader next to the title
+ * if the form is in a failed state
+ */
+ errorText?: string;
+ /** Success text to display as a subheader next to the title
+ * if the form has no errors and is in a Valid state
+ */
+ successText?: string;
+ /** This prop is controlled by taskModules.isFailedState
+ * Determines if the form is in a failed state where the user cannot continue
+ */
+ isFailedState?: boolean;
+}
+
+export interface AdditionalInformationProps extends HeaderProps {
+ Elements: React.ReactElement;
+}
+
+export interface AgentScriptProps extends HeaderProps {
+ /** Scripted Text for the user to read */
+ text: string;
+}
+
+export interface OutcomeProps extends HeaderProps {
+ /** SQForm Elements to render inside the Form */
+ FormElements: React.ReactElement;
+ /** Any props from MUI component */
+ muiGridProps?: GridProps;
+}
+
+export interface SQFormDataProps {
+ /** Form Entity Object aka initial values of the form */
+ initialValues: TValues;
+ /** Form Submission Handler | @typedef onSubmit: (values: Values, formikBag: FormikBag, context) => void | Promise */
+ onSubmit: (
+ values: TValues,
+ formikBag: FormikHelpers,
+ context: Context
+ ) => void | Promise;
+ /** Yup validation schema shape */
+ validationSchema: Record<
+ keyof TValues,
+ AnySchema
+ >;
+}
+
+export interface TaskModuleProps {
+ /** Unique name used as a key for managing expansion state within Accordion */
+ name: string;
+ /** Title text */
+ title: string;
+ /** The props used to configure SQForm */
+ formikProps: SQFormDataProps;
+ /** The props used to configured the Additional Information section */
+ additionalInformationSectionProps: AdditionalInformationProps;
+ /** The props used to configure the Scripted Text section */
+ scriptedTextProps: AgentScriptProps;
+ /** The props used to configure the Outcome form section */
+ outcomeProps: OutcomeProps;
+ /** Subtitle text - Each Subtitle is separated by a pipe "|" */
+ subtitles?: Array;
+ /** Panel is disabled, the user cannot toggle the panel while disabled */
+ isDisabled?: boolean;
+ /** Whether the panel is initially expanded */
+ isInitiallyExpanded?: boolean;
+ /** Whether the module is loading */
+ isLoading?: boolean;
+ /** Custom loading message to display when module is loading */
+ isLoadingMessage?: string;
+ /** Optional click handler if you want to perform a side effect on click */
+ onClick?: () => void;
+ /** Reset button text */
+ resetButtonText?: string;
+ /** Submit button text */
+ submitButtonText?: string;
+ /** Flag for if the form is in a failed state where the user cannot continue */
+ isFailedState?: boolean;
+ /** Whether the submit button is disabled */
+ isSubmitButtonDisabled?: boolean;
+}
+
+export interface SQFormGuidedWorkflowProps {
+ /** Main Title */
+ mainTitle: string;
+ /** Main Subtitle Informative Text */
+ mainSubtitle: string;
+ /** Task Module configuration Object(s) */
+ taskModules: Array>;
+ /** Number of tasks completed (Default is zero) */
+ initialCompletedTasks?: number;
+ /**
+ * Disables all Task Modules except the current Active Task module
+ * This prevents the user from returning to a previous task module
+ */
+ isStrictMode?: boolean;
+ /**
+ * Callback function which passes the error as an argument for the consumer to handle
+ * Usually the consumer will render an alert to signal an error occured
+ */
+ onError?: (error: Error) => void;
+ /** An object of css-in-js style properties to be passed */
+ containerStyles?: React.CSSProperties;
+}
+
+export type GuidedWorkflowDispatchActionType = {
+ type: 'UPDATE';
+ id: number;
+ data: TValues;
+};
+
+export type ManageTaskModulesUpdateActionType = {
+ type: 'UPDATE_ACTIVE_TASK_MODULE';
+ id: number;
+};
+
+export type ManageTaskModulesNextActionType = {
+ type: 'ENABLE_NEXT_TASK_MODULE';
+};
+
+export type ManageTaskModulesResetActionType = {
+ type: 'RESET_TO_INITIAL_STATE';
+};
+
+export type ManageTaskModulesActionType =
+ | ManageTaskModulesUpdateActionType
+ | ManageTaskModulesNextActionType
+ | ManageTaskModulesResetActionType;
+
+export type ManageTaskModulesState = {
+ activeTaskModuleID: number;
+ progressTaskModuleID: number;
+};
+
+export type Context = {
+ [key: number]: {
+ name: string;
+ data: TValues;
+ isDisabled: boolean;
+ };
+};
+
+export type UseGuidedWorkflowContextType = {
+ state: Context;
+ updateDataByID: (id: number, data: TValues) => void;
+};
+
+export type UseManageTaskModulesType = {
+ taskModulesState: ManageTaskModulesState;
+ updateActiveTaskModule: (taskNumber: number) => void;
+ enableNextTaskModule: () => void;
+};
diff --git a/src/components/SQFormGuidedWorkflow/index.js b/src/components/SQFormGuidedWorkflow/index.ts
similarity index 100%
rename from src/components/SQFormGuidedWorkflow/index.js
rename to src/components/SQFormGuidedWorkflow/index.ts
diff --git a/src/components/SQFormGuidedWorkflow/useGuidedWorkflowContext.js b/src/components/SQFormGuidedWorkflow/useGuidedWorkflowContext.js
deleted file mode 100644
index 4c2998b4..00000000
--- a/src/components/SQFormGuidedWorkflow/useGuidedWorkflowContext.js
+++ /dev/null
@@ -1,44 +0,0 @@
-import React from 'react';
-
-export function useGuidedWorkflowContext(taskModules) {
- const initialData = taskModules.reduce((acc, taskModule, index) => {
- return {
- ...acc,
- [index + 1]: {
- name: taskModule.name,
- data: taskModule.formikProps.initialValues,
- isDisabled: taskModule.isDisabled || false,
- },
- };
- }, {});
-
- const reducer = (prevState, action) => {
- switch (action.type) {
- case 'UPDATE':
- return taskModules.reduce((acc, taskModlue, index) => {
- const taskID = index + 1;
- const prevData = prevState[taskID].data;
- return {
- ...acc,
- [taskID]: {
- name: taskModlue.name,
- data: action.id === taskID ? action.data : prevData,
- isDisabled: taskModlue.isDisabled || false,
- },
- };
- }, {});
- default:
- throw new Error(
- `The ${action.type} type provided to useGuidedWorkflow is not valid`
- );
- }
- };
-
- const [state, dispatch] = React.useReducer(reducer, initialData);
-
- const updateDataByID = (id, data) => {
- dispatch({type: 'UPDATE', id, data});
- };
-
- return [state, updateDataByID];
-}
diff --git a/src/components/SQFormGuidedWorkflow/useGuidedWorkflowContext.ts b/src/components/SQFormGuidedWorkflow/useGuidedWorkflowContext.ts
new file mode 100644
index 00000000..ff526378
--- /dev/null
+++ b/src/components/SQFormGuidedWorkflow/useGuidedWorkflowContext.ts
@@ -0,0 +1,61 @@
+import React from 'react';
+import type {
+ TaskModuleProps,
+ Context,
+ GuidedWorkflowDispatchActionType,
+ UseGuidedWorkflowContextType,
+} from './Types';
+
+export function useGuidedWorkflowContext(
+ taskModules: Array>
+): UseGuidedWorkflowContextType {
+ const initialData = taskModules.reduce>(
+ (acc, taskModule, index) => {
+ return {
+ ...acc,
+ [index + 1]: {
+ name: taskModule.name,
+ data: taskModule.formikProps.initialValues,
+ isDisabled: taskModule.isDisabled || false,
+ },
+ };
+ },
+ {}
+ );
+
+ const reducer = (
+ prevState: Context,
+ action: GuidedWorkflowDispatchActionType
+ ) => {
+ switch (action.type) {
+ case 'UPDATE':
+ return taskModules.reduce>(
+ (acc, taskModlue, index) => {
+ const taskID = index + 1;
+ const prevData = prevState[taskID].data;
+ return {
+ ...acc,
+ [taskID]: {
+ name: taskModlue.name,
+ data: action.id === taskID ? action.data : prevData,
+ isDisabled: taskModlue.isDisabled || false,
+ },
+ };
+ },
+ {}
+ );
+ default:
+ throw new Error(
+ `The ${action.type} type provided to useGuidedWorkflow is not valid`
+ );
+ }
+ };
+
+ const [state, dispatch] = React.useReducer(reducer, initialData);
+
+ const updateDataByID = (id: keyof typeof initialData, data: TValues) => {
+ dispatch({type: 'UPDATE', id, data});
+ };
+
+ return {state, updateDataByID};
+}
diff --git a/src/components/SQFormGuidedWorkflow/useManageTaskModules.js b/src/components/SQFormGuidedWorkflow/useManageTaskModules.ts
similarity index 72%
rename from src/components/SQFormGuidedWorkflow/useManageTaskModules.js
rename to src/components/SQFormGuidedWorkflow/useManageTaskModules.ts
index 239ef076..3822220b 100644
--- a/src/components/SQFormGuidedWorkflow/useManageTaskModules.js
+++ b/src/components/SQFormGuidedWorkflow/useManageTaskModules.ts
@@ -1,16 +1,25 @@
import React from 'react';
-
-const getInitialTaskModuleID = (initialCompletedTasks, firstTaskModuleID) => {
- if (!!initialCompletedTasks) {
+import type {
+ Context,
+ ManageTaskModulesActionType,
+ ManageTaskModulesState,
+ UseManageTaskModulesType,
+} from './Types';
+
+const getInitialTaskModuleID = (
+ initialCompletedTasks: number,
+ firstTaskModuleID: number
+) => {
+ if (initialCompletedTasks) {
return initialCompletedTasks + 1;
}
return firstTaskModuleID;
};
-export function useManageTaskModules(
- initialCompletedTasks,
- taskModulesContext
-) {
+export function useManageTaskModules(
+ initialCompletedTasks: number,
+ taskModulesContext: Context
+): UseManageTaskModulesType {
const taskModulesLength = Object.keys(taskModulesContext).length;
const firstTaskModuleID = Number(Object.keys(taskModulesContext)[0]);
const initialTaskModuleID = getInitialTaskModuleID(
@@ -25,18 +34,25 @@ export function useManageTaskModules(
progressTaskModuleID: initialTaskModuleID,
};
- const manageTaskModulesReducer = (prevState, action) => {
- const isTaskIDTheFinalTask = (prevTaskID) => {
+ const manageTaskModulesReducer = (
+ prevState: ManageTaskModulesState,
+ action: ManageTaskModulesActionType
+ ): ManageTaskModulesState => {
+ const isTaskIDTheFinalTask = (prevTaskID: number | undefined) => {
return prevTaskID === taskModulesLength;
};
- const isTaskDisabled = (taskID) => {
- return taskModulesContext[taskID].isDisabled;
+ const isTaskDisabled = (taskID: number | undefined) => {
+ if (taskID) {
+ return taskModulesContext[taskID].isDisabled;
+ }
+
+ return true;
};
- const findNextTaskID = (prevTaskID) => {
+ const findNextTaskID = (prevTaskID: number): number => {
if (isTaskIDTheFinalTask(prevTaskID)) {
- return;
+ return -1;
}
const nextTaskID = prevTaskID + 1;
@@ -48,14 +64,14 @@ export function useManageTaskModules(
return nextTaskID;
};
- const incrementTaskModuleID = (prevTaskID) => {
+ const incrementTaskModuleID = (prevTaskID: number) => {
if (isTaskIDTheFinalTask(prevTaskID)) {
return prevTaskID;
}
const nextTaskModuleID = findNextTaskID(prevTaskID);
- if (nextTaskModuleID) {
+ if (nextTaskModuleID !== -1) {
return nextTaskModuleID;
}
@@ -78,8 +94,13 @@ export function useManageTaskModules(
};
};
- const updateActiveTaskModuleID = (id) => {
+ const updateActiveTaskModuleID = (id: number) => {
const nextID = findNextTaskID(id);
+ if (nextID === -1) {
+ return {
+ ...prevState,
+ };
+ }
if (isTaskDisabled(nextID)) {
const nextTaskID = findNextTaskID(nextID);
@@ -114,7 +135,7 @@ export function useManageTaskModules(
initialState
);
- const updateActiveTaskModule = (taskNumber) => {
+ const updateActiveTaskModule = (taskNumber: number) => {
if (taskModulesState.activeTaskModuleID !== taskNumber) {
updateTaskModulesState({
type: 'UPDATE_ACTIVE_TASK_MODULE',
diff --git a/stories/SQFormGuidedWorkflow.stories.js b/stories/SQFormGuidedWorkflow.stories.js
index 03aa854b..93d6d18b 100644
--- a/stories/SQFormGuidedWorkflow.stories.js
+++ b/stories/SQFormGuidedWorkflow.stories.js
@@ -197,7 +197,7 @@ const Template = () => {
>
),
title: 'Providers',
- actionButton: (
+ actions: (
window.alert('Awesome list here')}
diff --git a/stories/__tests__/SQFormGuidedWorkflow.stories.test.js b/stories/__tests__/SQFormGuidedWorkflow.stories.test.js
index f4c3bc16..f297d568 100644
--- a/stories/__tests__/SQFormGuidedWorkflow.stories.test.js
+++ b/stories/__tests__/SQFormGuidedWorkflow.stories.test.js
@@ -58,7 +58,7 @@ describe('SQFormGuidedWorkflow Tests', () => {
expect(nextButton).toBeDisabled();
});
- it('should enable next button when all required fields are filled out in the 1st section', () => {
+ it('should enable next button when all required fields are filled out in the 1st section', async () => {
render();
const nextButton = screen.getByRole('button', {name: /form submission/i});
@@ -70,7 +70,9 @@ describe('SQFormGuidedWorkflow Tests', () => {
const interested = screen.getByText('Interested');
userEvent.click(interested);
- expect(nextButton).toBeEnabled();
+ await waitFor(() => {
+ expect(nextButton).toBeEnabled();
+ });
});
it('should close current expanded card and open the next one when next button is clicked', async () => {
@@ -89,6 +91,9 @@ describe('SQFormGuidedWorkflow Tests', () => {
userEvent.click(interested);
const nextButton = screen.getByRole('button', {name: /form submission/i});
+ await waitFor(() => {
+ expect(nextButton).toBeEnabled();
+ });
userEvent.click(nextButton);
await waitForElementToBeRemoved(screen.getByTestId('loadingSpinner'), {
@@ -114,6 +119,9 @@ describe('SQFormGuidedWorkflow Tests', () => {
userEvent.click(interested);
const nextButton = screen.getByRole('button', {name: /form submission/i});
+ await waitFor(() => {
+ expect(nextButton).toBeEnabled();
+ });
userEvent.click(nextButton);
await waitForElementToBeRemoved(screen.getByTestId('loadingSpinner'), {
@@ -145,6 +153,9 @@ describe('SQFormGuidedWorkflow Tests', () => {
const interested = screen.getByText('Interested');
userEvent.click(interested);
const nextButton = screen.getByRole('button', {name: /form submission/i});
+ await waitFor(() => {
+ expect(nextButton).toBeEnabled();
+ });
userEvent.click(nextButton);
await waitForElementToBeRemoved(screen.getByTestId('loadingSpinner'), {
timeout: 3500,
@@ -195,6 +206,9 @@ describe('Testing new story', () => {
userEvent.type(textbox, 'Hello');
const nextButton = screen.getByRole('button', {name: /form submission/i});
+ await waitFor(() => {
+ expect(nextButton).toBeEnabled();
+ });
userEvent.click(nextButton);
await waitFor(