export function useStepsContext()

in src/hooks/useStepsContext/useStepsContext.ts [30:202]


export function useStepsContext(): IStepsContext {
  const [steps, setSteps] = useState<Steps>([]);

  const setStepName = useCallback((idx: number, name?: string) => {
    setSteps(oldSteps =>
      oldSteps.map((step, i) => {
        if (idx !== i) return step;
        step.name = name;
        return step;
      })
    );
  }, []);

  const onStepDetailChange = useCallback(
    (updatedStep: Step, indexToUpdate: number) =>
      setSteps(oldSteps =>
        oldSteps.map((currentStep, iterIndex) =>
          iterIndex === indexToUpdate ? { ...currentStep, ...updatedStep } : currentStep
        )
      ),
    []
  );

  return {
    steps,
    setSteps,
    setStepName,
    onDeleteAction: (targetStepIdx, indexToDelete) => {
      setSteps(steps =>
        steps.map((step, currentStepIndex) => {
          if (currentStepIndex !== targetStepIdx) return step;

          step.actions.splice(indexToDelete, 1);

          return { ...step, actions: [...step.actions] };
        })
      );
    },
    onSoftDeleteAction: (targetStepIdx, indexToDelete) => {
      setSteps(steps =>
        steps.map((step, currentStepIndex) => {
          if (currentStepIndex !== targetStepIdx) return step;

          return {
            ...step,
            actions: step.actions.map((actionContext, idx) =>
              // setting the `isModified` flag will cause the generator code to maintain the soft delete flag
              // when playwright tries to overwrite the action
              idx === indexToDelete
                ? {
                    ...actionContext,
                    modified: true,
                    isSoftDeleted: true,
                  }
                : actionContext
            ),
          };
        })
      );
    },
    onDeleteStep: stepIndex => {
      setSteps([...steps.slice(0, stepIndex), ...steps.slice(stepIndex + 1)]);
    },
    onInsertAction: (action, targetStepIdx, indexToInsert) => {
      setSteps(
        steps.map((step, currentStepIndex) => {
          if (currentStepIndex !== targetStepIdx) return step;

          step.actions.splice(indexToInsert, 0, action);

          return { name: step.name, actions: [...step.actions] };
        })
      );
    },
    onSetActionIsOpen: (stepIndex, actionIndex, isOpen) => {
      setSteps(
        steps.map((step, currentStepIndex) => {
          if (currentStepIndex !== stepIndex) return step;
          const actions = step.actions.map((action, currentActionIndex) => {
            if (currentActionIndex !== actionIndex) return action;
            return { ...action, isOpen };
          });
          return { name: step.name, actions };
        })
      );
    },
    onMergeSteps: (indexToInsert, indexToRemove) =>
      setSteps(oldSteps => [
        ...oldSteps.slice(0, indexToInsert),
        {
          name: oldSteps[indexToInsert].name ?? undefined,
          actions: [...steps[indexToInsert].actions, ...steps[indexToRemove].actions],
        },
        ...oldSteps.slice(indexToRemove + 1, oldSteps.length),
      ]),
    onRearrangeSteps: (indexA, indexB) => {
      setSteps(oldSteps => {
        const placeholder = steps[indexA];
        oldSteps[indexA] = oldSteps[indexB];
        oldSteps[indexB] = placeholder;
        return oldSteps;
      });
    },
    /**
     * Used in conjunction with the drag/drop functionality for
     * moving a step separator in the UI to reorganize the bucketing
     * of actions.
     *
     * @example
     * The examples below depend on this array:
     *
     * [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
     *
     *
     * Example 1:
     * Target index: 0
     * Initiator index: 1
     * Action index: 1
     * Output: [[1, 2], [3, 4, 5, 6], [7, 8, 9]]
     *
     * Slice step 0 at idx 1, results in [1, 2]
     * Slice step 0, starting at idx 2...length, results in [3]
     * Merge [3] with step idx 1, [4, 5, 6]
     *
     * Example 2:
     * Target index: 1
     * Initiator index: 1
     * Action index: 0
     * Output: [[1, 2, 3, 4], [5, 6], [7, 8, 9]]
     *
     * Example 3:
     * Target index: 2
     * Initiator index: 1
     * Action index: 0
     * Output: [[1, 2, 3], [4, 5], [6, 7, 8, 9]]
     */
    onDropStep: onDropStep(steps, setSteps),
    onSplitStep: (stepIndex, actionIndex) => {
      if (actionIndex === 0) {
        throw Error(`Cannot remove all actions from a step.`);
      }
      if (steps.length <= stepIndex) {
        throw Error('Step index cannot exceed steps length.');
      }
      const stepToSplit = steps[stepIndex];
      if (stepToSplit.actions.length <= 1) {
        throw Error('Cannot split step with only one action.');
      }
      const reducedStepActions = stepToSplit.actions.slice(0, actionIndex);
      const insertedStepActions = stepToSplit.actions.slice(actionIndex);

      setSteps([
        ...steps.slice(0, stepIndex),
        { name: stepToSplit.name, actions: reducedStepActions },
        { actions: insertedStepActions },
        ...steps.slice(stepIndex + 1, steps.length),
      ]);
    },
    onStepDetailChange,
    onUpdateAction: (action: ActionInContext, stepIndex: number, actionIndex: number) => {
      const step = steps[stepIndex];
      onStepDetailChange(
        {
          actions: step.actions.map((curAction, curIdx) =>
            curIdx === actionIndex ? action : curAction
          ),
          name: step.name,
        },
        stepIndex
      );
    },
  };
}