formik#FormikValues TypeScript Examples

The following examples show how to use formik#FormikValues. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: test-utils.tsx    From abacus with GNU General Public License v2.0 7 votes vote down vote up
MockFormik = ({
  children,
  initialValues = {},
}: {
  children: React.ReactNode
  initialValues?: FormikValues
}) => {
  return (
    <Formik initialValues={initialValues} onSubmit={() => undefined}>
      {children}
    </Formik>
  )
}
Example #2
Source File: useResetPasswordFormik.tsx    From bouncecode-cms with GNU General Public License v3.0 7 votes vote down vote up
function useResetPasswordFormik(
  onSubmit: (
    values: FormikValues,
    formikHelpers: FormikHelpers<FormikValues>,
  ) => void | Promise<any>,
  options?: Partial<FormikConfig<FormikValues>>,
) {
  const {enqueueSnackbar} = useSnackbar();

  const formik = useFormik({
    ...options,
    initialValues: {
      ...initialValues,
      ...options?.initialValues,
    },
    validationSchema,
    onSubmit,
  });

  useEffect(() => {
    if (formik.submitCount > 0 && !formik.isSubmitting && !formik.isValid) {
      enqueueSnackbar('누락된 입력 항목을 확인해주세요.', {
        variant: 'error',
      });
    }
  }, [formik.submitCount, formik.isSubmitting]);

  return formik;
}
Example #3
Source File: useInitialRequiredErrors.tsx    From SQForm with MIT License 7 votes vote down vote up
// Until Formik exposes the validationSchema (again) via useFormikContext(), 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
export function useInitialRequiredErrors<Values>(
  validationSchema: AnyObjectSchema | undefined,
  initialValues: FormikValues
): FormikErrors<Values> {
  const initialRequiredErrorsRef = React.useRef(
    Object.entries(validationSchema || {}).reduce((acc, [key, value]) => {
      if (value?._exclusive?.required && !_getHasValue(initialValues[key])) {
        return {...acc, [key]: 'Required'};
      }
      return acc;
    }, {})
  );

  return initialRequiredErrorsRef.current;
}
Example #4
Source File: SQFormReadOnly.tsx    From SQForm with MIT License 6 votes vote down vote up
function SQFormReadOnly<Values extends FormikValues>({
  readOnlyFields,
  initialValues,
  enableReinitialize = false,
  muiGridProps = {},
}: SQFormReadOnlyProps<Values>): JSX.Element {
  return (
    <Formik<Values>
      initialValues={initialValues}
      onSubmit={noop}
      enableReinitialize={enableReinitialize}
    >
      {(_props) => {
        return (
          <Form>
            <Grid
              {...muiGridProps}
              container
              spacing={muiGridProps.spacing ?? 2}
            >
              {readOnlyFields.map((readOnlyField, index) => {
                const props = {
                  key: readOnlyField?.key ?? index,
                  muiFieldProps: noBottomMarginStyle,
                  ...readOnlyField,
                };

                if (readOnlyField?.mask) {
                  return <SQFormMaskedReadOnlyField {...props} />;
                }
                return <SQFormReadOnlyField {...props} />;
              })}
            </Grid>
          </Form>
        );
      }}
    </Formik>
  );
}
Example #5
Source File: useSignUpFormik.tsx    From bouncecode-cms with GNU General Public License v3.0 6 votes vote down vote up
function useSignUpFormik(
  onSubmit: (
    values: FormikValues,
    formikHelpers: FormikHelpers<FormikValues>,
  ) => void | Promise<any>,
  options?: Partial<FormikConfig<FormikValues>>,
) {
  const {enqueueSnackbar} = useSnackbar();

  const formik = useFormik({
    ...options,
    initialValues: {
      ...initialValues,
      ...options?.initialValues,
    },
    validationSchema,
    onSubmit,
  });

  useEffect(() => {
    if (formik.submitCount > 0 && !formik.isSubmitting && !formik.isValid) {
      enqueueSnackbar('누락된 입력 항목을 확인해주세요.', {
        variant: 'error',
      });
    }
  }, [formik.submitCount, formik.isSubmitting]);

  return formik;
}
Example #6
Source File: useSignInFormik.tsx    From bouncecode-cms with GNU General Public License v3.0 6 votes vote down vote up
function useSigninFormik(
  onSubmit: (
    values: FormikValues,
    formikHelpers: FormikHelpers<FormikValues>,
  ) => void | Promise<any>,
  options?: Partial<FormikConfig<FormikValues>>,
) {
  const {enqueueSnackbar} = useSnackbar();

  const formik = useFormik({
    ...options,
    initialValues: {
      ...initialValues,
      ...options?.initialValues,
    },
    validationSchema,
    onSubmit,
  });

  useEffect(() => {
    if (formik.submitCount > 0 && !formik.isSubmitting && !formik.isValid) {
      enqueueSnackbar('누락된 입력 항목을 확인해주세요.', {
        variant: 'error',
      });
    }
  }, [formik.submitCount, formik.isSubmitting]);

  return formik;
}
Example #7
Source File: useResetPasswordSubmitCallback.tsx    From bouncecode-cms with GNU General Public License v3.0 6 votes vote down vote up
function useResetPasswordSubmitCallback() {
  const {enqueueSnackbar} = useSnackbar();

  return useCallback(
    async (
      values: FormikValues,
      formikHelpers: FormikHelpers<FormikValues>,
    ) => {
      try {
        // await firebase.auth().sendPasswordResetEmail(values.email);
        enqueueSnackbar('이메일을 확인해주세요.', {variant: 'success'});
      } catch (e) {
        console.error(e);
        enqueueSnackbar(e.message, {variant: 'error'});
      }
    },
    [],
  );
}
Example #8
Source File: index.ts    From SQForm with MIT License 6 votes vote down vote up
// Note - Formik fields expect empty strings and not nulls

// When initializing the forms initialValues we can use this function to replace all nulls with empty strings
export function sanitizeInitialValues(valuesObject: FormikValues): string {
  return JSON.parse(
    JSON.stringify(valuesObject, (_key, value) => {
      return value === null ? '' : value;
    })
  );
}
Example #9
Source File: index.ts    From SQForm with MIT License 6 votes vote down vote up
// If we want to send null to our API instead of empty strings
// Note - If this becomes a common scenario we can make this the default behavior for SQForm onSubmit
export function serializeFormValues(formValues: FormikValues): string {
  return JSON.parse(
    JSON.stringify(formValues, (_key, value) => {
      return value === '' ? null : value;
    })
  );
}
Example #10
Source File: SQForm.tsx    From SQForm with MIT License 5 votes vote down vote up
function SQForm<Values extends FormikValues>({
  children,
  enableReinitialize = false,
  initialValues,
  muiGridProps = {},
  onSubmit,
  validationSchema,
}: SQFormProps<Values>): JSX.Element {
  const initialErrors = useInitialRequiredErrors(
    validationSchema,
    initialValues
  );

  // HACK: This is a workaround for: https://github.com/mui-org/material-ui-pickers/issues/2112
  // Remove this reset handler when the issue is fixed.
  const handleReset = () => {
    document &&
      document.activeElement &&
      (document.activeElement as HTMLElement).blur();
  };

  const handleSubmit = useDebouncedCallback(
    (values: Values, formikHelpers: FormikHelpers<Values>) =>
      onSubmit(values, formikHelpers),
    500,
    {leading: true, trailing: false}
  );

  return (
    <Formik<Values>
      enableReinitialize={enableReinitialize}
      initialErrors={initialErrors}
      initialValues={initialValues}
      onSubmit={handleSubmit}
      onReset={handleReset}
      validationSchema={validationSchema}
      validateOnMount={true}
    >
      {(_props) => {
        return (
          <Form>
            <Grid
              {...muiGridProps}
              container
              spacing={muiGridProps.spacing ?? 2}
            >
              {children}
            </Grid>
          </Form>
        );
      }}
    </Formik>
  );
}
Example #11
Source File: useCommentCreateFormik.tsx    From bouncecode-cms with GNU General Public License v3.0 5 votes vote down vote up
export function useCommentCreateFormik(
  postId: string,
  options?: Partial<FormikConfig<FormikValues>>,
) {
  const {enqueueSnackbar} = useSnackbar();
  const [create] = useCommentCreateMutation({
    onCompleted: () => {
      formik.handleReset(null);
    },
  });

  const onSubmit = async (
    values: FormikValues,
    formikHelpers: FormikHelpers<FormikValues>,
  ) => {
    return create({
      variables: {
        data: {
          postId,
          text: values.text,
          payload: {},
        },
      },
      refetchQueries: [CommentsDocument, CommentStatDocument],
    });
  };

  const formik = useFormik({
    ...options,
    initialValues: {
      ...initialValues,
      ...options?.initialValues,
    },
    validationSchema,
    onSubmit,
  });

  useEffect(() => {
    if (formik.submitCount > 0 && !formik.isSubmitting && !formik.isValid) {
      enqueueSnackbar('누락된 입력 항목을 확인해주세요.', {
        variant: 'error',
      });
    }
  }, [formik.submitCount, formik.isSubmitting]);

  return formik;
}
Example #12
Source File: index.tsx    From youtube-2020-june-multi-step-form-formik with MIT License 5 votes vote down vote up
export function FormikStepper({ children, ...props }: FormikConfig<FormikValues>) {
  const childrenArray = React.Children.toArray(children) as React.ReactElement<FormikStepProps>[];
  const [step, setStep] = useState(0);
  const currentChild = childrenArray[step];
  const [completed, setCompleted] = useState(false);

  function isLastStep() {
    return step === childrenArray.length - 1;
  }

  return (
    <Formik
      {...props}
      validationSchema={currentChild.props.validationSchema}
      onSubmit={async (values, helpers) => {
        if (isLastStep()) {
          await props.onSubmit(values, helpers);
          setCompleted(true);
        } else {
          setStep((s) => s + 1);

          // the next line was not covered in the youtube video
          //
          // If you have multiple fields on the same step
          // we will see they show the validation error all at the same time after the first step!
          //
          // If you want to keep that behaviour, then, comment the next line :)
          // If you want the second/third/fourth/etc steps with the same behaviour
          //    as the first step regarding validation errors, then the next line is for you! =)
          //
          // In the example of the video, it doesn't make any difference, because we only
          //    have one field with validation in the second step :)
          helpers.setTouched({});
        }
      }}
    >
      {({ isSubmitting }) => (
        <Form autoComplete="off">
          <Stepper alternativeLabel activeStep={step}>
            {childrenArray.map((child, index) => (
              <Step key={child.props.label} completed={step > index || completed}>
                <StepLabel>{child.props.label}</StepLabel>
              </Step>
            ))}
          </Stepper>

          {currentChild}

          <Grid container spacing={2}>
            {step > 0 ? (
              <Grid item>
                <Button
                  disabled={isSubmitting}
                  variant="contained"
                  color="primary"
                  onClick={() => setStep((s) => s - 1)}
                >
                  Back
                </Button>
              </Grid>
            ) : null}
            <Grid item>
              <Button
                startIcon={isSubmitting ? <CircularProgress size="1rem" /> : null}
                disabled={isSubmitting}
                variant="contained"
                color="primary"
                type="submit"
              >
                {isSubmitting ? 'Submitting' : isLastStep() ? 'Submit' : 'Next'}
              </Button>
            </Grid>
          </Grid>
        </Form>
      )}
    </Formik>
  );
}
Example #13
Source File: index.tsx    From youtube-2020-june-material-ui-themes with MIT License 5 votes vote down vote up
export function FormikStepper({
  children,
  ...props
}: FormikConfig<FormikValues>) {
  const childrenArray = React.Children.toArray(children) as React.ReactElement<
    FormikStepProps
  >[];
  const [step, setStep] = useState(0);
  const currentChild = childrenArray[step];
  const [completed, setCompleted] = useState(false);

  function isLastStep() {
    return step === childrenArray.length - 1;
  }

  return (
    <Formik
      {...props}
      validationSchema={currentChild.props.validationSchema}
      onSubmit={async (values, helpers) => {
        if (isLastStep()) {
          await props.onSubmit(values, helpers);
          setCompleted(true);
        } else {
          setStep(s => s + 1);
        }
      }}
    >
      {({ isSubmitting }) => (
        <Form autoComplete="off">
          <Stepper alternativeLabel activeStep={step}>
            {childrenArray.map((child, index) => (
              <Step
                key={child.props.label}
                completed={step > index || completed}
              >
                <StepLabel>{child.props.label}</StepLabel>
              </Step>
            ))}
          </Stepper>

          {currentChild}

          <Grid container spacing={2}>
            {step > 0 ? (
              <Grid item>
                <Button
                  disabled={isSubmitting}
                  variant="contained"
                  color="primary"
                  onClick={() => setStep(s => s - 1)}
                >
                  Back
                </Button>
              </Grid>
            ) : null}
            <Grid item xs={12}>
              <Button
                startIcon={
                  isSubmitting ? <CircularProgress size="1rem" /> : null
                }
                disabled={isSubmitting}
                type="submit"
                fullWidth
              >
                {isSubmitting ? "Submitting" : isLastStep() ? "Submit" : "NeXt"}
              </Button>
            </Grid>
          </Grid>
        </Form>
      )}
    </Formik>
  );
}
Example #14
Source File: SQFormStoryWrapper.tsx    From SQForm with MIT License 5 votes vote down vote up
function Form({
  children,
  initialValues,
  validationSchema,
  muiGridProps,
  showSubmit = true,
}: SQFormStoryWrapperProps): React.ReactElement {
  const [value, setValue] = React.useState({});

  const [snackbarState, {snackbar, closeSnackBar}] = useSnackbar();
  const handleSubmit = (values: FormikValues) => {
    setValue(values);
  };

  const firstUpdate = React.useRef(true);
  React.useLayoutEffect(() => {
    if (firstUpdate.current) {
      firstUpdate.current = false;
    } else {
      snackbar.success(
        <pre style={{fontSize: '14px', margin: 0}}>
          {JSON.stringify(value, null, 2)}
        </pre>
      );
    }
  }, [snackbar, value]);

  return (
    <>
      <SQForm
        initialValues={initialValues}
        validationSchema={validationSchema}
        muiGridProps={{wrap: 'nowrap', ...muiGridProps}}
        onSubmit={handleSubmit}
      >
        {children}
        {showSubmit && (
          <Grid item={true} sm={2} style={{alignSelf: 'center'}}>
            <SQFormButton>Submit</SQFormButton>
          </Grid>
        )}
      </SQForm>
      <Snackbar snackbarState={snackbarState} closeSnackBar={closeSnackBar} />
    </>
  );
}
Example #15
Source File: SQFormDialog.tsx    From SQForm with MIT License 5 votes vote down vote up
function SQFormDialog<Values extends FormikValues>({
  cancelButtonText = 'Cancel',
  children,
  disableBackdropClick = false,
  isDisabled = false,
  isOpen,
  maxWidth = 'sm',
  onClose,
  onSave,
  shouldDisplaySaveButton = true,
  saveButtonText = 'Save',
  tertiaryButtonText,
  title,
  enableReinitialize = false,
  initialValues,
  muiGridProps = {},
  shouldRequireFieldUpdates = false,
  validationSchema,
  showSecondaryButton = true,
  onTertiaryClick,
  tertiaryStatus = 'HIDE_BUTTON',
  tertiaryButtonVariant = 'outlined',
}: SQFormDialogProps<Values>): React.ReactElement {
  const initialErrors = useInitialRequiredErrors(
    validationSchema,
    initialValues
  );

  return (
    <Formik
      enableReinitialize={enableReinitialize}
      initialErrors={initialErrors}
      initialValues={initialValues}
      onSubmit={onSave}
      validationSchema={validationSchema}
      validateOnMount={true}
    >
      <SQFormDialogInner<Values>
        cancelButtonText={cancelButtonText}
        children={children}
        disableBackdropClick={disableBackdropClick}
        isDisabled={isDisabled}
        isOpen={isOpen}
        maxWidth={maxWidth}
        onClose={onClose}
        shouldDisplaySaveButton={shouldDisplaySaveButton}
        saveButtonText={saveButtonText}
        shouldRequireFieldUpdates={shouldRequireFieldUpdates}
        title={title}
        muiGridProps={muiGridProps}
        showSecondaryButton={showSecondaryButton}
        tertiaryStatus={tertiaryStatus}
        tertiaryButtonText={tertiaryButtonText}
        onTertiaryClick={onTertiaryClick}
        tertiaryButtonVariant={tertiaryButtonVariant}
      />
    </Formik>
  );
}
Example #16
Source File: SQFormGuidedWorkflow.tsx    From SQForm with MIT License 4 votes vote down vote up
function SQFormGuidedWorkflow<TValues extends FormikValues>({
  taskModules,
  mainTitle,
  mainSubtitle,
  initialCompletedTasks = 0,
  isStrictMode = false,
  onError,
  containerStyles = {},
}: SQFormGuidedWorkflowProps<TValues>): 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?: SQFormGuidedWorkflowDataProps<TValues>['validationSchema']
  ) => {
    if (validationSchema?.fields) {
      const validationFields =
        validationSchema.fields as Yup.ObjectSchema<TValues>;
      return Object.entries(validationFields).reduce((acc, [key, value]) => {
        if (value.tests[0]?.OPTIONS.name === 'required') {
          return {...acc, [key]: 'Required'};
        }
        return acc;
      }, {});
    }

    return {};
  };

  const classes = useStyles();

  const {
    state: taskModulesContext,
    updateDataByID: updateTaskModuleContextByID,
  } = useGuidedWorkflowContext<TValues>(taskModules);

  const {taskModulesState, updateActiveTaskModule, enableNextTaskModule} =
    useManageTaskModules<TValues>(initialCompletedTasks, taskModulesContext);

  const transformedTaskModules = taskModules.map((taskModule, index) => {
    const taskNumber = index + 1;
    const taskName = taskModule.name;
    const validationYupSchema = taskModule.formikProps?.validationSchema;
    const initialErrors = getFormikInitialRequiredErrors(
      taskModule.formikProps?.validationSchema
    );
    const isPanelExpanded =
      taskModulesContext[taskModulesState.activeTaskModuleID].name === taskName;

    const getIsDisabled = () => {
      if (isStrictMode && taskModulesState.activeTaskModuleID !== taskNumber) {
        return true;
      }
      if (
        taskModule.isDisabled ||
        taskModulesState.progressTaskModuleID < taskNumber
      ) {
        return true;
      }
      return false;
    };

    const handleSubmit = async (
      values: TValues,
      formikBag: FormikHelpers<TValues>
    ) => {
      const context = {
        ...taskModulesContext,
        [taskNumber]: {
          ...taskModulesContext[taskNumber],
          data: values,
        },
      };

      try {
        await taskModule.formikProps.onSubmit(values, formikBag, context);
        updateTaskModuleContextByID(taskNumber, values);
        enableNextTaskModule();
      } catch (error) {
        onError && onError(error as Error);
      }
    };

    return {
      ...taskModule,
      isDisabled: getIsDisabled(),
      isInitiallyExpanded: taskModule.isInitiallyExpanded || isPanelExpanded,
      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: (
        <Formik
          enableReinitialize={true}
          initialErrors={initialErrors}
          initialValues={taskModulesContext[taskNumber].data}
          onSubmit={handleSubmit}
          validationSchema={validationYupSchema}
          validateOnMount={true}
        >
          {({isSubmitting}) => (
            <Form>
              <CardContent className={classes.root}>
                {isSubmitting || taskModule.isLoading ? (
                  <div data-testid="loadingSpinner">
                    <ComponentLoadingSpinner
                      message={
                        isSubmitting
                          ? `Saving ${taskModule.title} outcome`
                          : taskModule.isLoadingMessage
                      }
                    />
                  </div>
                ) : (
                  <>
                    {taskModule.additionalInformationSectionProps && (
                      <AdditionalInformationSection
                        {...taskModule.additionalInformationSectionProps}
                        isFailedState={taskModule.isFailedState}
                      />
                    )}
                    <AgentScript
                      {...taskModule.scriptedTextProps}
                      isFailedState={taskModule.isFailedState}
                    />
                    <OutcomeForm
                      {...taskModule.outcomeProps}
                      isFailedState={taskModule.isFailedState}
                    />
                  </>
                )}
              </CardContent>
              <CardActions className={classes.panelFooter}>
                <SQFormButton type="reset" title="Reset Form">
                  {taskModule?.resetButtonText ?? 'Reset'}
                </SQFormButton>
                <SQFormButton
                  shouldRequireFieldUpdates={true}
                  isDisabled={
                    taskModule.isFailedState ||
                    taskModule?.isSubmitButtonDisabled ||
                    isSubmitting
                  }
                >
                  {taskModule?.submitButtonText ?? 'Next'}
                </SQFormButton>
              </CardActions>
            </Form>
          )}
        </Formik>
      ),
    };
  });

  return (
    <Section style={{padding: '20px', ...containerStyles}}>
      <SectionHeader title={mainTitle} informativeHeading={mainSubtitle} />
      <SectionBody>
        <Accordion accordionPanels={transformedTaskModules} />
      </SectionBody>
    </Section>
  );
}
Example #17
Source File: SQFormDialogStepper.tsx    From SQForm with MIT License 4 votes vote down vote up
export function SQFormDialogStepper<Values extends FormikValues>({
  cancelButtonText = 'Cancel',
  children,
  disableBackdropClick = false,
  isOpen = false,
  isNextDisabled = false,
  maxWidth = 'sm',
  onClose,
  onSubmit,
  title,
  enableReinitialize = false,
  muiGridProps = {},
  dialogProps,
  setValues,
  fullWidth = true,
  contentStyle,
  initialValues,
  ...props
}: SQFormDialogStepperProps<Values>): JSX.Element {
  const steps = children;
  const [activeStep, setActiveStep] = React.useState(0);
  const currentChild = steps[activeStep];
  const [completed, setCompleted] = React.useState<number[]>([]);

  const validationSchema = currentChild.props.validationSchema || null;

  const classes = useStyles({steps});
  const actionsClasses = useActionsStyles();
  const stepperClasses = useStepperStyles();

  const isLastStep = React.useMemo(() => {
    return activeStep === steps.length - 1;
  }, [activeStep, steps]);

  const handleNext = () => {
    setActiveStep(activeStep + 1);
    setCompleted([...completed, activeStep]);
  };

  const handleStep = (step: number) => () => {
    if (completed.includes(step)) {
      setActiveStep(step);
    }
  };

  const handleSubmit = async (
    values: Values,
    helpers: FormikHelpers<Values>
  ) => {
    if (isLastStep) {
      await onSubmit(values, helpers);
    } else {
      setValues && setValues(values);
      handleNext();
    }
  };

  function SubmitButton() {
    const {errors, values, dirty} = useFormikContext<Record<string, unknown>>();

    const isButtonDisabled = React.useMemo(() => {
      if (isNextDisabled) {
        return true;
      }
      if (!validationSchema) {
        return false;
      }
      const currentStepKeys = Object.keys(validationSchema.fields);
      const stepValues = currentStepKeys.every((step) => {
        return !!values[step];
      });

      if (
        !stepValues ||
        currentStepKeys.some((step) => Object.keys(errors).includes(step)) ||
        !dirty
      ) {
        return true;
      }

      return false;
    }, [errors, values, dirty]);

    const primaryButtonText = isLastStep ? 'Submit' : 'Next';
    return (
      <RoundedButton
        type="submit"
        isDisabled={isButtonDisabled}
        title={primaryButtonText}
      >
        {primaryButtonText}
      </RoundedButton>
    );
  }

  const handleClose = (
    event: Record<string, unknown>,
    reason: 'backdropClick' | 'escapeKeyDown' | 'cancelClick'
  ) => {
    if (disableBackdropClick && reason === 'backdropClick') {
      return;
    }

    onClose(event, reason);
  };

  return (
    <Formik
      {...props}
      validationSchema={validationSchema}
      onSubmit={handleSubmit}
      initialValues={initialValues}
      enableReinitialize={enableReinitialize}
    >
      {() => (
        <Dialog
          TransitionComponent={Transition}
          disableBackdropClick={disableBackdropClick}
          maxWidth={maxWidth}
          open={isOpen}
          onClose={handleClose}
          fullWidth={fullWidth}
          {...dialogProps}
        >
          <Form>
            <DialogTitle disableTypography={true}>
              <Typography variant="h4">{title}</Typography>
            </DialogTitle>
            <Divider />
            {steps.length > 1 && (
              <Grid container classes={stepperClasses}>
                <Stepper
                  activeStep={activeStep}
                  classes={classes}
                  alternativeLabel={true}
                >
                  {steps.map((child, index) => (
                    <Step key={`${child.props.label}-${index}`}>
                      <StepButton onClick={handleStep(index)}>
                        <Typography
                          variant="overline"
                          color={index === activeStep ? 'error' : 'initial'} // sets the color to orange if current step
                        >
                          {child?.props.label}
                        </Typography>
                      </StepButton>
                    </Step>
                  ))}
                </Stepper>
              </Grid>
            )}
            <DialogContent
              dividers
              style={{
                paddingTop: '40px',
                paddingBottom: '40px',
                ...contentStyle,
              }}
            >
              <Grid
                {...muiGridProps}
                container
                spacing={muiGridProps.spacing ?? 3}
                justifyContent="center"
              >
                {currentChild}
              </Grid>
            </DialogContent>
            <DialogActions classes={actionsClasses}>
              <RoundedButton
                title={cancelButtonText}
                onClick={onClose}
                color="secondary"
                variant="outlined"
              >
                {cancelButtonText}
              </RoundedButton>
              <SubmitButton />
            </DialogActions>
          </Form>
        </Dialog>
      )}
    </Formik>
  );
}
Example #18
Source File: SQFormDialogInner.tsx    From SQForm with MIT License 4 votes vote down vote up
function SQFormDialogInner<Values extends FormikValues>({
  cancelButtonText,
  children,
  disableBackdropClick,
  isDisabled = false,
  isOpen,
  maxWidth,
  onClose,
  saveButtonText,
  tertiaryButtonText,
  shouldRequireFieldUpdates = false,
  title,
  muiGridProps,
  shouldDisplaySaveButton = true,
  showSecondaryButton = true,
  tertiaryStatus = 'HIDE_BUTTON',
  onTertiaryClick,
  tertiaryButtonVariant,
}: SQFormDialogInnerProps<Values>): React.ReactElement {
  const theme = useTheme();
  const titleClasses = useTitleStyles(theme);
  const actionsClasses = useActionsStyles(theme);
  const primaryActionsClasses = usePrimaryActionStyles(theme);
  const dialogContentClasses = useDialogContentStyles(theme);
  const formikContext = useFormikContext<Values>();

  function getIsDisabled() {
    switch (tertiaryStatus) {
      case 'IS_DISABLED':
        return true;
      case 'FORM_VALIDATION_ONLY':
        return !formikContext.isValid;
      default:
        return false;
    }
  }

  const {
    isDialogOpen: isDialogAlertOpen,
    openDialog: openDialogAlert,
    closeDialog: closeDialogAlert,
  } = useDialog();

  const handleCancel = (
    event: Record<string, unknown>,
    reason: 'backdropClick' | 'escapeKeyDown' | 'cancelClick'
  ) => {
    if (disableBackdropClick && reason === 'backdropClick') {
      return;
    }

    if (!formikContext.dirty) {
      onClose && onClose(event, reason);
    } else {
      openDialogAlert();
    }
  };

  const confirmCancel = () => {
    formikContext.resetForm();
    onClose && onClose({}, 'escapeKeyDown');
    closeDialogAlert();
  };

  const renderTertiaryButton = () => {
    return (
      <Grid
        container={true}
        justifyContent={showSecondaryButton ? 'space-between' : 'flex-end'}
      >
        {showSecondaryButton && (
          <Grid item={true}>
            <RoundedButton
              title={cancelButtonText}
              onClick={(event: Record<string, unknown>) =>
                handleCancel(event, 'cancelClick')
              }
              color="secondary"
              variant="outlined"
            >
              {cancelButtonText}
            </RoundedButton>
          </Grid>
        )}

        <Grid item={true}>
          <span style={{paddingRight: '20px'}}>
            <RoundedButton
              title={tertiaryButtonText}
              isDisabled={getIsDisabled()}
              onClick={() => onTertiaryClick?.(formikContext)}
              type="button"
              variant={tertiaryButtonVariant}
            >
              {tertiaryButtonText}
            </RoundedButton>
          </span>
          {shouldDisplaySaveButton && (
            <SQFormButton
              title={saveButtonText}
              isDisabled={isDisabled}
              shouldRequireFieldUpdates={shouldRequireFieldUpdates}
            >
              {saveButtonText}
            </SQFormButton>
          )}
        </Grid>
      </Grid>
    );
  };

  return (
    <>
      <Dialog
        maxWidth={maxWidth}
        open={isOpen}
        TransitionComponent={Transition}
        onClose={
          showSecondaryButton || disableBackdropClick ? handleCancel : undefined
        }
      >
        <Form>
          <DialogTitle disableTypography={true} classes={titleClasses}>
            <Typography variant="h4">{title}</Typography>
          </DialogTitle>
          <DialogContent classes={dialogContentClasses}>
            <Grid
              {...muiGridProps}
              container
              spacing={muiGridProps?.spacing ?? 2}
            >
              {children}
            </Grid>
          </DialogContent>
          <DialogActions
            classes={
              showSecondaryButton ? actionsClasses : primaryActionsClasses
            }
          >
            {tertiaryStatus !== 'HIDE_BUTTON'
              ? renderTertiaryButton()
              : showSecondaryButton && (
                  <RoundedButton
                    title={cancelButtonText}
                    onClick={(event: Record<string, unknown>) =>
                      handleCancel(event, 'cancelClick')
                    }
                    color="secondary"
                    variant="outlined"
                  >
                    {cancelButtonText}
                  </RoundedButton>
                )}
            {tertiaryStatus === 'HIDE_BUTTON' && shouldDisplaySaveButton && (
              <SQFormButton
                title={saveButtonText}
                isDisabled={isDisabled}
                shouldRequireFieldUpdates={shouldRequireFieldUpdates}
              >
                {saveButtonText}
              </SQFormButton>
            )}
          </DialogActions>
        </Form>
      </Dialog>

      <DialogAlert
        isOpen={isDialogAlertOpen}
        primaryButtonText="Continue"
        secondaryButtonText="Go Back"
        onPrimaryButtonClick={confirmCancel}
        onSecondaryButtonClick={closeDialogAlert}
        title={`Cancel Changes`}
      >
        You currently have unsaved changes which will be lost if you continue.
      </DialogAlert>
    </>
  );
}
Example #19
Source File: EditHabitModal.tsx    From nyxo-app with GNU General Public License v3.0 4 votes vote down vote up
EditHabitModal = () => {
  const show = useAppSelector(({ modal }) => modal.editHabit)
  const habit = useAppSelector(getDraftEditHabit)
  const dispatch = useAppDispatch()

  if (!habit) return null

  const {
    title = '',
    description = '',
    period = Period.morning,
    dayStreak = 0,
    longestDayStreak = 0
  } = habit

  const closeModal = () => {
    dispatch(toggleEditHabitModal(false))
  }

  const submitHabitChange = ({
    title: newTitle,
    description: newDescription,
    period: newPeriod
  }: FormikValues) => {
    dispatch(
      editHabitThunk(newTitle, newDescription, newPeriod, habit as Habit)
    )
    dispatch(toggleEditHabitModal(false))
  }

  return (
    <StyledModal
      hideModalContentWhileAnimating
      isVisible={show}
      useNativeDriver>
      <Formik
        validateOnMount
        initialValues={{
          title,
          description,
          period
        }}
        validationSchema={HabitSchema}
        onSubmit={submitHabitChange}>
        {({ handleChange, handleSubmit, handleBlur, values, isValid }) => (
          <>
            <Container>
              <HabitModalTopRow
                closeModal={closeModal}
                saveModal={handleSubmit}
                isValid={isValid}
                formattedTitle={title}
              />
              <ScrollView>
                <HabitModalStreak
                  dayStreak={dayStreak}
                  longestDayStreak={longestDayStreak}
                />
                <HabitModalFieldSection
                  handleChangeText={handleChange('title')}
                  onBlur={handleBlur('title')}
                  indicatorText="HABIT.TITLE_FIELD_INDICATOR"
                  placeholder="HABIT.TITLE_PLACEHOLDER"
                  inputMaxLength={titleMaxLength}
                  value={values.title}
                  isTitle
                />

                <HabitModalFieldSection
                  handleChangeText={handleChange('description')}
                  onBlur={handleBlur('description')}
                  indicatorText="HABIT.DESCRIPTION_FIELD_INDICATOR"
                  placeholder="HABIT.DESCRIPTION_PLACEHOLDER"
                  inputMaxLength={descriptionMaxLength}
                  value={revertLineBreaks(values.description)}
                  isTitle={false}
                />

                <HabitModalTimeSection
                  period={values.period}
                  setPeriod={handleChange('period')}
                />
              </ScrollView>
            </Container>
          </>
        )}
      </Formik>
    </StyledModal>
  )
}