react-hook-form#Controller TypeScript Examples

The following examples show how to use react-hook-form#Controller. 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: SideDrawerField.tsx    From firetable with Apache License 2.0 7 votes vote down vote up
export default function Email({
  control,
  column,
  disabled,
}: ISideDrawerFieldProps) {
  return (
    <Controller
      control={control}
      name={column.key}
      render={({ onChange, onBlur, value }) => {
        return (
          <TextField
            variant="filled"
            fullWidth
            margin="none"
            placeholder={column.name}
            onChange={onChange}
            onBlur={onBlur}
            value={value}
            id={`sidedrawer-field-${column.key}`}
            label=""
            hiddenLabel
            disabled={disabled}
            inputProps={{
              autoComplete: "email",
              maxLength: column.config?.maxLength,
            }}
          />
        );
      }}
    />
  );
}
Example #2
Source File: CustomFields.stories.tsx    From react-hook-form-generator with MIT License 6 votes vote down vote up
ReactSelectField = ({ name, label, placeholder, options }) => {
  const { control } = useFormContext();

  return (
    <FormControl>
      <FormLabel>{label}</FormLabel>
      <Controller
        name={name}
        control={control}
        as={
          <Select
            placeholder={placeholder}
            options={options}
          />
        }
        onChange={([selected]) => {
          return selected;
        }}
      />
    </FormControl>
  );
}
Example #3
Source File: controlled-checkbox.tsx    From tams-club-cal with MIT License 6 votes vote down vote up
ControlledCheckbox = (props: ControlledCheckboxProps) => {
    useEffect(() => {
        if (!props.value) return;
        props.setValue(props.name, props.value);
    }, [props.value]);

    return (
        <Controller
            control={props.control}
            name={props.name}
            defaultValue={props.value}
            render={({ field: { onChange, onBlur, value } }) => (
                <FormControlLabel
                    control={<Checkbox />}
                    label={props.label}
                    disabled={props.disabled}
                    onChange={onChange}
                    onBlur={onBlur}
                    checked={value}
                    sx={props.sx}
                />
            )}
        />
    );
}
Example #4
Source File: DateSelector.tsx    From backstage with Apache License 2.0 6 votes vote down vote up
DateSelector = ({ name, control, setValue }: Props) => {
  const label = `${
    name.charAt(0).toLocaleUpperCase('en-US') + name.slice(1, name.indexOf('D'))
  } date`;

  return (
    <Controller
      name={name}
      control={control}
      render={({ field }) => (
        <FormControl>
          <MuiPickersUtilsProvider utils={LuxonUtils}>
            <KeyboardDatePicker
              disableToolbar
              format="dd-MM-yyyy"
              label={label}
              value={field.value}
              onChange={date => {
                setValue(name, date?.toISO());
              }}
              InputProps={{
                endAdornment: (
                  <IconButton onClick={() => setValue(name, null)}>
                    <ClearIcon />
                  </IconButton>
                ),
              }}
              InputAdornmentProps={{
                position: 'start',
              }}
            />
          </MuiPickersUtilsProvider>
        </FormControl>
      )}
    />
  );
}
Example #5
Source File: SwitchElement.tsx    From react-hook-form-mui with MIT License 6 votes vote down vote up
export default function SwitchElement({ name, control, ...other }: SwitchElementProps) {
  return (
    <FormControlLabel
      control={
        <Controller
          name={name}
          control={control}
          render={({ field }) => <Switch {...field} checked={field.value} />}
        />
      }
      {...other}
    />
  )
}
Example #6
Source File: TimestampEditor.tsx    From firebase-tools-ui with Apache License 2.0 6 votes vote down vote up
TimestampEditor: React.FC<
  React.PropsWithChildren<{
    value: firebase.firestore.Timestamp;
    onChange: (value: firebase.firestore.Timestamp) => void;
    name: string;
  }>
> = ({ value, onChange, name }) => {
  return (
    <Controller
      name={name}
      rules={{
        validate: (e) => {
          return !isNaN(Date.parse(e)) || 'Must be a date-time';
        },
      }}
      render={({ field: { ref, ...field }, fieldState }) => (
        <Field
          label="Value"
          type="datetime-local"
          step={1}
          defaultValue={dateToLocale(value.toDate())}
          error={fieldState.error?.message}
          {...field}
          onChange={(e) => {
            const timestamp = Date.parse(e.currentTarget.value);
            field.onChange(e.currentTarget.value);
            if (!isNaN(timestamp) && timestamp !== value.toMillis()) {
              onChange(firebase.firestore.Timestamp.fromMillis(timestamp));
            }
          }}
        />
      )}
    />
  );
}
Example #7
Source File: Form.tsx    From plasmic with MIT License 6 votes vote down vote up
export function FormTextInput({
  name,
  children,
  defaultValue,
}: {
  name?: string;
  children: any;
  defaultValue?: string;
}) {
  const contexts = useAllContexts();
  const form = useContext(FormContext);
  let isLoading = false;
  //check loading
  if (!!defaultValue && isContextValueRef(defaultValue)) {
    const { contextName } = getContextAndField(defaultValue);
    //const context = useContext(contextTable[contextName]);
    //isLoading = !context;
    isLoading = !contexts[contextName];
  }

  if (!form || !name || isLoading) {
    return React.cloneElement(children, {
      ...children.props,
      value: "Loading...",
    });
  }

  return (
    <Controller
      name={name}
      control={form.control}
      defaultValue={getPropValue(defaultValue ?? "", contexts)}
      render={({ field }) =>
        React.cloneElement(children, { ...children.props, ...field, name })
      }
    />
  );
}
Example #8
Source File: LabeledRadio.tsx    From UsTaxes with GNU Affero General Public License v3.0 6 votes vote down vote up
export function LabeledRadio<A>(props: LabeledRadioProps<A>): ReactElement {
  const { label, name, values, useGrid = true, sizes = { xs: 12 } } = props

  const classes = useStyles()
  const { control } = useFormContext<A>()

  return (
    <ConditionallyWrap
      condition={useGrid}
      wrapper={(children) => (
        <Grid item {...sizes}>
          {children}
        </Grid>
      )}
    >
      <Controller
        name={name}
        render={({ field: { value, onChange } }) => (
          <div className={classes.root}>
            <FormControl component="fieldset">
              <FormLabel>{label}</FormLabel>
              <RadioGroup name={name} value={value} onChange={onChange}>
                {values.map(([rowLabel, rowValue], i) => (
                  <FormControlLabel
                    key={i}
                    value={rowValue}
                    control={<Radio color="primary" />}
                    label={rowLabel}
                  />
                ))}
              </RadioGroup>
            </FormControl>
          </div>
        )}
        control={control}
      />
    </ConditionallyWrap>
  )
}
Example #9
Source File: ReadOnlyInput.tsx    From calories-in with MIT License 6 votes vote down vote up
function ReadOnlyInput({
  name,
  inputType,
  nutritionValueUnit,
  isBold = false,
}: Props) {
  return (
    <Controller
      name={name}
      render={({ field }) => {
        let { value } = field

        if (inputType === 'foodCategory') {
          const foodCategory = foodCategories.find(({ id }) => id === value)
          if (foodCategory) {
            value = foodCategory.name
          }
        }

        return (
          <Text
            fontWeight={isBold ? 'semibold' : 'normal'}
            fontSize={isBold ? 'lg' : 'md'}
          >
            {inputType === 'nutritionValue'
              ? `${formatNutritionValue(value)}${nutritionValueUnit}`
              : value}
          </Text>
        )
      }}
    />
  )
}
Example #10
Source File: formItem.tsx    From Search-Next with GNU General Public License v3.0 6 votes vote down vote up
FormItem: React.FC<FormItemProps> = (props) => {
  const {
    name,
    children,
    size,
    rules,
    defaultValue = '',
    label,
    control,
  } = props;

  return (
    <div style={{ padding: '6px 0' }}>
      <Controller
        name={name}
        control={control}
        rules={rules}
        defaultValue={defaultValue}
        render={({
          field: { onChange: controllerOnChange, ref, ...field },
          fieldState: { error },
        }) =>
          React.cloneElement(children, {
            onChange: (e: any) => {
              if (children.props.onChange) children.props.onChange(e);
              controllerOnChange(e);
            },
            label,
            size,
            inputRef: ref,
            ...field,
            error: !!error,
            helperText: error ? error.message : '',
          })
        }
      />
    </div>
  );
}
Example #11
Source File: SideDrawerField.tsx    From firetable with Apache License 2.0 5 votes vote down vote up
export default function Action({
  column,
  control,
  docRef,
  disabled,
}: ISideDrawerFieldProps) {
  const classes = useStyles();
  const fieldClasses = useFieldStyles();

  const row = useWatch({ control });

  return (
    <Controller
      control={control}
      name={column.key}
      render={({ onChange, value }) => {
        const hasRan = value && value.status;

        return (
          <Grid container alignItems="center" wrap="nowrap" spacing={2}>
            <Grid item xs className={classes.labelGridItem}>
              <div className={fieldClasses.root}>
                <Typography variant="body1" className={classes.label}>
                  {hasRan && isUrl(value.status) ? (
                    <Link
                      href={value.status}
                      target="_blank"
                      rel="noopener noreferrer"
                      variant="body2"
                      underline="always"
                    >
                      {value.status}
                    </Link>
                  ) : hasRan ? (
                    value.status
                  ) : (
                    sanitiseCallableName(column.key)
                  )}
                </Typography>
              </div>
            </Grid>

            <Grid item>
              <ActionFab
                row={{ ...row, ref: docRef }}
                column={{ config: column.config, key: column.key }}
                onSubmit={onChange}
                value={value}
                disabled={disabled}
              />
            </Grid>
          </Grid>
        );
      }}
    />
  );
}
Example #12
Source File: NumberField.tsx    From react-hook-form-generator with MIT License 5 votes vote down vote up
NumberField: FC<FieldProps<NumberFieldSchema>> = ({
  id,
  name,
  field,
  defaultValue,
}) => {
  const {
    label,
    placeholder,
    helperText,
    isRequired,
    shouldDisplay,
    styles = {},
  } = field;

  const fieldStyles = useStyles<FieldStyles>('numberField', styles);

  const { control, watch } = useFormContext();

  const values = watch(name);

  const errorMessage = useErrorMessage(name, label);

  const isVisible = useMemo(() => {
    return shouldDisplay ? shouldDisplay(values) : true;
  }, [values, shouldDisplay]);

  return isVisible ? (
    <FormControl
      key={`${name}-control`}
      isRequired={isRequired}
      isInvalid={!!errorMessage}
      {...fieldStyles.control}
    >
      {!!label && (
        <FormLabel htmlFor={name} {...fieldStyles.errorMessage}>
          {label}
        </FormLabel>
      )}
      <Controller
        name={name}
        control={control}
        defaultValue={defaultValue || 0}
        as={
          <NumberInput>
            <NumberInputField
              id={id}
              data-testid={id}
              placeholder={placeholder}
              {...fieldStyles.input}
            />
            <NumberInputStepper>
              <NumberIncrementStepper />
              <NumberDecrementStepper />
            </NumberInputStepper>
          </NumberInput>
        }
      />
      {!!helperText && (
        <FormHelperText {...fieldStyles.helperText}>
          {helperText}
        </FormHelperText>
      )}
      <FormErrorMessage {...fieldStyles.errorMessage}>
        {errorMessage}
      </FormErrorMessage>
    </FormControl>
  ) : null;
}
Example #13
Source File: index.tsx    From taskcafe with MIT License 5 votes vote down vote up
AddUserPopup: React.FC<AddUserPopupProps> = ({ onAddUser }) => {
  const {
    register,
    handleSubmit,
    formState: { errors },
    setError,
    control,
  } = useForm<CreateUserData>();

  const createUser = (data: CreateUserData) => {
    onAddUser(data, setError);
  };
  return (
    <CreateUserForm onSubmit={handleSubmit(createUser)}>
      <AddUserInput
        width="100%"
        label="Full Name"
        variant="alternate"
        {...register('fullName', { required: 'Full name is required' })}
      />
      {errors.fullName && <InputError>{errors.fullName.message}</InputError>}
      <AddUserInput
        floatingLabel
        width="100%"
        label="Email"
        variant="alternate"
        {...register('email', { required: 'Email is required' })}
      />
      {errors.email && <InputError>{errors.email.message}</InputError>}
      <Controller
        control={control}
        name="roleCode"
        rules={{ required: 'Role is required' }}
        render={({ field }) => (
          <Select
            {...field}
            label="Role"
            options={[
              { label: 'Admin', value: 'admin' },
              { label: 'Member', value: 'member' },
            ]}
          />
        )}
      />
      {errors.roleCode && errors.roleCode.value && <InputError>{errors.roleCode.value.message}</InputError>}
      <AddUserInput
        floatingLabel
        width="100%"
        label="Username"
        variant="alternate"
        {...register('username', { required: 'Username is required' })}
      />
      {errors.username && <InputError>{errors.username.message}</InputError>}
      <AddUserInput
        floatingLabel
        width="100%"
        label="Initials"
        variant="alternate"
        {...register('initials', { required: 'Initials is required' })}
      />
      {errors.initials && <InputError>{errors.initials.message}</InputError>}
      <AddUserInput
        floatingLabel
        width="100%"
        label="Password"
        variant="alternate"
        type="password"
        {...register('password', { required: 'Password is required' })}
      />
      {errors.password && <InputError>{errors.password.message}</InputError>}
      <CreateUserButton type="submit">Create</CreateUserButton>
    </CreateUserForm>
  );
}
Example #14
Source File: CreditCardForm.tsx    From storefront with MIT License 5 votes vote down vote up
CreditCardForm = React.forwardRef<HTMLFormElement, Props>(({ onSubmit }, ref) => {
  const innerRef = useRef<HTMLFormElement>(null);

  const { control, formState, handleSubmit } = useForm<CreditCardFormData>({
    defaultValues: { ccNumber: '', ccExp: '', ccCsc: '' },
  });

  // Override standard form methods, this works for now:
  useImperativeHandle(ref, () => ({
    ...(innerRef.current ?? new HTMLFormElement()),
    checkValidity: () => innerRef.current?.checkValidity() ?? false,
    reportValidity: () => innerRef.current?.reportValidity() ?? false,
    submit: () => {
      handleSubmit(onSubmit)();
    },
  }));

  return (
    <form ref={innerRef} onSubmit={handleSubmit(onSubmit)}>
      <Controller
        control={control}
        name="ccNumber"
        render={({ field }) => (
          <NumberFormat
            fullWidth
            required
            autoComplete="cc-number"
            customInput={TextField}
            error={'ccNumber' in formState.errors}
            format="#### #### #### ####"
            label="Number"
            margin="normal"
            {...field}
          />
        )}
      />
      <Grid container spacing={2}>
        <Grid item xs={6}>
          <Controller
            control={control}
            name="ccExp"
            render={({ field }) => (
              <NumberFormat
                fullWidth
                required
                autoComplete="cc-csc"
                customInput={TextField}
                error={'ccExp' in formState.errors}
                format="## / ##"
                label="Expiry date"
                margin="normal"
                {...field}
              />
            )}
          />
        </Grid>
        <Grid item xs={6}>
          <Controller
            control={control}
            name="ccCsc"
            render={({ field }) => (
              <NumberFormat
                fullWidth
                required
                autoComplete="cc-csc"
                customInput={TextField}
                error={'ccCsc' in formState.errors}
                format="###"
                label="CVC"
                margin="normal"
                {...field}
              />
            )}
          />
        </Grid>
      </Grid>
    </form>
  );
})
Example #15
Source File: InputSelector.tsx    From backstage with Apache License 2.0 5 votes vote down vote up
InputSelector = ({ name, options, control, error }: Props) => {
  const label = name.charAt(0).toLocaleUpperCase('en-US') + name.slice(1);

  return (
    <Controller
      name={name}
      control={control}
      rules={{
        required: true,
      }}
      render={({ field }) => (
        <FormControl fullWidth>
          <InputLabel
            required
            htmlFor="demo-simple-select-outlined"
            id="demo-simple-select-outlined-label"
            style={{
              marginTop: '0.25rem',
            }}
          >
            {label}
          </InputLabel>
          <Select
            {...field}
            labelId="demo-simple-select-outlined-label"
            id="demo-simple-select-outlined"
            label={label}
            error={!!error}
          >
            {options.map(option => {
              return (
                <MenuItem button key={option} value={option}>
                  {option}
                </MenuItem>
              );
            })}
          </Select>
        </FormControl>
      )}
    />
  );
}
Example #16
Source File: DelegationChangeProposalForm.tsx    From homebase-app with MIT License 5 votes vote down vote up
DelegationChangeProposalForm: React.FC<Props> = ({ open, handleClose, defaultValues }) => {
  const daoId = useDAOID();
  const { data: dao } = useDAO(daoId);
  const { data: daoHoldings } = useDAOHoldings(daoId);

  const methods = useForm<Values>({
    defaultValues: useMemo(
      () => ({
        newDelegationAddress: "",
        ...defaultValues,
      }),
      [defaultValues]
    ),
    // resolver: yupResolver(validationSchema as any),
  });

  const newDelegationAddress = methods.watch("newDelegationAddress");

  useEffect(() => {
    methods.reset(defaultValues);
  }, [defaultValues, methods]);

  const { mutate } = useProposeDelegationChange();

  const onSubmit = useCallback(
    (values: Values) => {
      if (dao) {
        mutate({ dao, newDelegationAddress: values.newDelegationAddress });
        handleClose();
      }
    },
    [dao, handleClose, mutate]
  );

  return (
    <FormProvider {...methods}>
      <ResponsiveDialog
        open={open}
        onClose={handleClose}
        aria-labelledby='alert-dialog-title'
        aria-describedby='alert-dialog-description'
        title={"Change Delegate"}>
        <Content container direction={"column"} style={{ gap: 18 }}>
          <Grid item>
            <ProposalFormInput label={"New Delegate Address"}>
              <Controller
                control={methods.control}
                name={`newDelegationAddress`}
                render={({ field }) => (
                  <TextField
                    {...field}
                    type='text'
                    placeholder=' tz1...'
                    InputProps={{ disableUnderline: true }}
                  />
                )}
              />
            </ProposalFormInput>
          </Grid>

          <Grid item>
            <Typography align='left' variant='subtitle2' color='textPrimary' display={"inline"}>
              Proposal Fee:{" "}
            </Typography>
            <Typography align='left' variant='subtitle2' color='secondary' display={"inline"}>
              {dao && dao.data.extra.frozen_extra_value.toString()} {dao ? dao.data.token.symbol : ""}
            </Typography>
          </Grid>

          <SendButton
            onClick={methods.handleSubmit(onSubmit as any)}
            disabled={!dao || !daoHoldings || !newDelegationAddress}>
            Submit
          </SendButton>
        </Content>
      </ResponsiveDialog>
    </FormProvider>
  );
}
Example #17
Source File: CheckboxElement.tsx    From react-hook-form-mui with MIT License 5 votes vote down vote up
export default function CheckboxElement({
  name,
  validation = {},
  required,
  parseError,
  label,
  control,
  ...rest
}: CheckboxElementProps): JSX.Element {

  if (required) {
    validation.required = 'This field is required'
  }

  return (
    <Controller
      name={name}
      rules={validation}
      control={control}
      render={({ field: { value, onChange }, fieldState: { invalid, error } }) => {
        const helperText = error ? (typeof parseError === 'function' ? parseError(error) : error.message) : rest.helperText
        return (
          <FormControl required={required} error={invalid}>
            <FormGroup row>
              <FormControlLabel
                label={label || ''}
                control={
                  <Checkbox
                    color={'primary'}
                    sx={{
                      color: invalid ? "error.main" : undefined,
                    }}
                    value={value}
                    checked={!!value}
                    onChange={() => {
                      onChange(!value)
                      //setValue(name, !formValue, { shouldValidate: true })
                    }}
                  />
                }
              />
            </FormGroup>
            {helperText && <FormHelperText error={invalid}>{helperText}</FormHelperText>}
          </FormControl>
        )
      }}
    />
  )
}
Example #18
Source File: CollectionFilter.tsx    From firebase-tools-ui with Apache License 2.0 5 votes vote down vote up
ConditionSelect: React.FC<React.PropsWithChildren<unknown>> = ({
  children,
}) => {
  const options: Array<{
    label: string;
    value: firebase.firestore.WhereFilterOp;
  }> = [
    {
      label: '(==) equal to',
      value: '==',
    },
    {
      label: '(!=) not equal to',
      value: '!=',
    },
    {
      label: '(>) greater than',
      value: '>',
    },
    {
      label: '(>=) greater than or equal to',
      value: '>=',
    },
    {
      label: '(<) less than',
      value: '<',
    },
    {
      label: '(<=) less than or equal to',
      value: '<=',
    },
    {
      label: '(in) equal to any of the following',
      value: 'in',
    },
    {
      label: '(not-in) not equal to any of the following',
      value: 'not-in',
    },
    {
      label: '(array-contains) an array containing',
      value: 'array-contains',
    },
    {
      label: '(array-contains-any) an array containing any',
      value: 'array-contains-any',
    },
  ];

  return (
    <>
      <Controller
        name="operator"
        render={({ field: { ref, ...field } }) => (
          <SelectField
            label="Only show documents where the specified field is..."
            placeholder="No condition"
            options={options}
            {...field}
            onChange={(selected) => {
              field.onChange(selected.currentTarget.value || undefined);
            }}
          />
        )}
      />
      {children}
    </>
  );
}
Example #19
Source File: FormTextField.tsx    From rn-credit-card with MIT License 5 votes vote down vote up
FormTextField = React.forwardRef<TextInput, Props>((props, ref) => {
  const {
    name,
    rules,
    validationLength = 1,
    formatter,
    onBlur,
    onValid,
    ...restOfProps
  } = props
  const { control, formState, trigger, watch } = useFormContext()
  const value = watch(name)

  useEffect(() => {
    async function validate() {
      const isValid = await trigger(name)
      if (isValid) onValid?.()
    }

    if (value?.length >= validationLength) {
      validate()
    }
  }, [value, name, validationLength, trigger]) // eslint-disable-line react-hooks/exhaustive-deps

  return (
    <Controller
      control={control}
      render={({ field }) => (
        <TextField
          // passing everything down to TextField
          // to be able to support all TextInput props
          {...restOfProps}
          ref={ref}
          testID={`TextField.${name}`}
          errorText={formState.errors[name]?.message}
          onBlur={(event) => {
            field.onBlur()
            onBlur?.(event)
          }}
          onChangeText={(text) => {
            const formatted = formatter ? formatter(field.value, text) : text
            field.onChange(formatted)
          }}
          value={field.value}
        />
      )}
      name={name}
      rules={rules}
    />
  )
})
Example #20
Source File: index.tsx    From admin with MIT License 5 votes vote down vote up
ControlledCheckbox = ({
  control,
  name,
  id,
  index,
  value,
  ...props
}: ControlledCheckboxProps) => {
  const variants = useWatch<string[]>({
    control,
    name,
  })

  return (
    <Controller
      control={control}
      name={name}
      render={(field) => {
        return (
          <Checkbox
            className="shrink-0 inter-small-regular"
            {...props}
            {...field}
            checked={variants?.some((variant) => variant === value)}
            onChange={(e) => {
              // copy field value
              const valueCopy = [...(variants || [])] as any[]

              // update checkbox value
              valueCopy[index] = e.target.checked ? id : null

              // update field value
              field.onChange(valueCopy)
            }}
          />
        )
      }}
    />
  )
}
Example #21
Source File: ContactForm.tsx    From keycapsets.com with GNU General Public License v3.0 5 votes vote down vote up
function ContactForm(): JSX.Element {
    const { register, handleSubmit, errors, control, reset } = useForm<ContactKCSInputs>();
    const close = useModalStore((s) => s.controls.close);
    const [loading, setLoading] = useState(false);

    async function submitContact(formValues: ContactKCSInputs, e: any) {
        setLoading(true);
        const body = {
            ...formValues,
            source: 'PROMOTE_YOUR_SET',
        };
        try {
            const URL = 'https://api.keycapsets.com/contact-kcs';
            await fetch(URL, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify(body),
            });
            setLoading(false);
            e.target.reset();
        } catch (err) {
            console.log(err);
            setLoading(false);
        }
    }

    return (
        <div className="column">
            <h2>Contact</h2>
            <br />
            <form onSubmit={handleSubmit(submitContact)} className="contact-form">
                <Input
                    id="name"
                    label="Full name"
                    reference={register({ required: `I'd like to know your name.` })}
                    className={errors.name ? 'invalid' : ''}
                    errorMessage={errors.name && (errors.name.message as string)}
                />

                <Input
                    id="email"
                    label="Email address"
                    reference={register({
                        required: 'Email address is required',
                        pattern: {
                            value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i,
                            message: 'Email address is invalid',
                        },
                    })}
                    defaultValue={user.email}
                    className={errors.email ? 'invalid' : ''}
                    errorMessage={errors.email && (errors.email.message as string)}
                />

                <Controller
                    as={TextArea}
                    control={control}
                    id="text"
                    name="text"
                    label="Message"
                    reference={register({ required: 'Let me know where you want to talk about!' })}
                    errorMessage={errors.text && (errors.text.message as string)}
                />

                <div className="controls flex v-center">
                    <button className="btn secondary md" onClick={close}>
                        Cancel
                    </button>
                    <button className="btn primary md" type="submit">
                        {loading ? 'Loading...' : 'Send'}
                    </button>
                </div>
            </form>
        </div>
    );
}
Example #22
Source File: LabeledCheckbox.tsx    From UsTaxes with GNU Affero General Public License v3.0 5 votes vote down vote up
export function LabeledCheckbox<TFormValues>(
  props: LabeledCheckboxProps<TFormValues>
): ReactElement {
  const { label, name, useGrid = true, sizes = { xs: 12 } } = props
  const { control } = useFormContext<TFormValues>()

  return (
    <ConditionallyWrap
      condition={useGrid}
      wrapper={(children) => (
        <Grid item {...sizes}>
          {children}
        </Grid>
      )}
    >
      <Controller
        name={name}
        render={({ field: { value, onChange } }) => (
          <FormControl component="fieldset">
            <FormGroup>
              <FormControlLabel
                control={
                  <Checkbox
                    name={name}
                    checked={value as boolean}
                    onChange={(_, checked) => onChange(checked)}
                    color="primary"
                  />
                }
                label={label}
                value={value}
              />
            </FormGroup>
          </FormControl>
        )}
        control={control}
      />
    </ConditionallyWrap>
  )
}
Example #23
Source File: VolumeFormFields.tsx    From calories-in with MIT License 5 votes vote down vote up
function VolumeFields({ canEdit, food }: Props) {
  const { register } = useFormContext<FoodForm>()
  const { portionsById, volumeBasedPortions } = usePortions()
  const portion = food?.volume ? portionsById[food.volume.portionId] : undefined

  return (
    <Flex minHeight={canEdit ? '200px' : undefined} flexDirection="column">
      {canEdit && (
        <Alert status="info" mb={3}>
          <AlertIcon color="teal.400" />
          Enter the food weight in grams per some volume measurement if you want
          to convert between weight and volume (for example: between grams and
          cups).
        </Alert>
      )}

      <HStack spacing={2}>
        {canEdit && <Text fontWeight="medium">1 x</Text>}
        {canEdit ? (
          <PortionsSelect
            width="200px"
            portions={volumeBasedPortions}
            {...register('volumeForm.portionId')}
          />
        ) : (
          <Text fontWeight="medium">
            1 x {`${portion?.singular} (${portion?.millilitersPerAmount} ml)`}
          </Text>
        )}

        <Text fontWeight="medium">=</Text>
        <HStack spacing={1} alignItems="center">
          {canEdit ? (
            <Controller
              name="volumeForm.weightInGrams"
              render={({ field }) => (
                <AmountInput value={field.value} onChange={field.onChange} />
              )}
            />
          ) : (
            <Text>{food?.volume?.weightInGrams}g</Text>
          )}
          {canEdit && <Text textColor="gray.500">g</Text>}
        </HStack>
      </HStack>
    </Flex>
  )
}
Example #24
Source File: APIBasicDetailsForm.tsx    From one-platform with MIT License 4 votes vote down vote up
APIBasicDetailsForm = forwardRef<HTMLDivElement, Props>(
  ({ onSearchOwners }: Props, ref): JSX.Element => {
    const { control, trigger } = useFormContext<FormData>();

    // PFE select selected value could be either string or object
    // Object when its a select from the list of provided
    // String when creating or deleting
    const onSelectOwner = (
      onChange: (...event: any[]) => void,
      value: FormData['owners'],
      selected: string | SelectOptionObject,
      isPlaceholder?: boolean
    ) => {
      if (isPlaceholder) return;

      const isSelectedString = typeof selected === 'string';
      const id =
        typeof selected === 'string' ? selected : (selected as { rhatUUID: string }).rhatUUID;
      const isSelected = value.findIndex(({ mid, email }) => mid === id || email === id) !== -1;

      if (isSelected) {
        onChange(value.filter(({ mid, email }) => mid !== id && email !== id));
      } else if (isSelectedString) {
        onChange([...value, { group: ApiEmailGroup.MAILING_LIST, mid: selected, email: selected }]);
      } else {
        onChange([
          ...value,
          { group: ApiEmailGroup.USER, mid: id, email: (selected as { mail: string }).mail },
        ]);
      }
      // What it does: This triggers a validation on owner field after onChange
      // Why: when react hook form in blur validation mode. The multi select has a text field and a selected view component
      // On clicking select onblur is triggered before onchange on select view causing the validtion to fail
      // thus onchange i trigger validation again
      trigger('owners', { shouldFocus: false });
    };

    return (
      <Card ref={ref}>
        <CardTitle>
          <Title headingLevel="h1">Add API to Catalog</Title>
        </CardTitle>
        <CardBody>
          <Stack hasGutter>
            <StackItem>
              <Controller
                control={control}
                name="name"
                defaultValue=""
                render={({ field, fieldState: { error } }) => (
                  <FormGroup
                    label="API Source name"
                    fieldId="api-source-name"
                    validated={error ? 'error' : 'success'}
                    helperTextInvalid={error?.message}
                  >
                    <TextInput
                      id="api-source-name"
                      placeholder="Give a name for the API datasource"
                      {...field}
                    />
                  </FormGroup>
                )}
              />
            </StackItem>
            <StackItem>
              <Controller
                control={control}
                name="description"
                defaultValue=""
                render={({ field, fieldState: { error } }) => (
                  <FormGroup
                    label="Description"
                    fieldId="api-desc-name"
                    validated={error ? 'error' : 'success'}
                    helperTextInvalid={error?.message}
                  >
                    <TextArea
                      id="api-source-name"
                      placeholder="Give a name for the API datasource"
                      {...field}
                    />
                  </FormGroup>
                )}
              />
            </StackItem>
            <StackItem>
              <Controller
                control={control}
                name="owners"
                defaultValue={[]}
                render={({ field: { onChange, value, onBlur, name }, fieldState: { error } }) => (
                  <FormGroup
                    label="Owners"
                    fieldId="api-owners"
                    validated={error ? 'error' : 'success'}
                    helperTextInvalid={error?.message}
                  >
                    <AsyncSelect
                      render={debouncePromise(onSearchOwners)}
                      variant={SelectVariant.typeaheadMulti}
                      isCreatable
                      // onCreateOption={(mid) =>
                      //   onChange([...value, { group: ApiEmailGroup.MAILING_LIST, mid, email: mid }])
                      // }
                      onSelect={(_, selected, isPlaceholder) =>
                        onSelectOwner(onChange, value, selected, isPlaceholder)
                      }
                      name={name}
                      onBlur={onBlur}
                      selections={value.map(({ email }) => email)}
                      createText="Use mailing list: "
                      placeholderText="Search by kerberos id or add mailing list"
                      customFilter={() => true}
                      maxHeight="320px"
                    />
                  </FormGroup>
                )}
              />
            </StackItem>
          </Stack>
        </CardBody>
      </Card>
    );
  }
)
Example #25
Source File: SideDrawerField.tsx    From firetable with Apache License 2.0 4 votes vote down vote up
export default function Slider({
  control,
  column,
  disabled,
}: ISideDrawerFieldProps) {
  const config: {
    max: number;
    min: number;
    minLabel?: string;
    maxLabel?: string;
    step: number;
    unit: string;
    marks?: boolean;
  } = {
    max: 10,
    step: 1,
    units: "",
    min: 0,
    ...(column as any).config,
  };
  const { max, marks, min, unit, minLabel, maxLabel, step } = config;

  return (
    <Controller
      control={control}
      name={column.key}
      render={({ onChange, onBlur, value }) => {
        const handleChange = (_: any, value: number | number[]) => {
          onChange(value);
          onBlur();
        };

        const getAriaValueText = (value: number) =>
          `${value}${unit ? " " + unit : ""}`;

        const getValueLabelFormat = (value: number) =>
          `${value}${unit ? " " + unit : ""}`;

        return (
          <Grid container spacing={2} alignItems="center">
            <Grid item>
              <Typography
                variant="overline"
                component="span"
                color="textSecondary"
              >
                {minLabel ?? `${min}${unit ? " " + unit : ""}`}
              </Typography>
            </Grid>

            <Grid item xs>
              <MuiSlider
                valueLabelDisplay="auto"
                min={min}
                max={max}
                marks={marks}
                step={step ?? 1}
                getAriaValueText={getAriaValueText}
                valueLabelFormat={getValueLabelFormat}
                value={value ?? min}
                onClick={onBlur}
                onChange={handleChange}
                disabled={disabled}
                style={{ display: "block" }}
              />
            </Grid>

            <Grid item>
              <Typography
                variant="overline"
                component="span"
                color="textSecondary"
              >
                {maxLabel ?? `${max}${unit ? " " + unit : ""}`}
              </Typography>
            </Grid>
          </Grid>
        );
      }}
    />
  );
}
Example #26
Source File: SetUp.tsx    From atlas with GNU General Public License v3.0 4 votes vote down vote up
SetUp: React.FC<SetUpProps> = ({
  selectedType,
  activeInputs,
  setActiveInputs,
  maxEndDate,
  maxStartDate,
  handleGoForward,
}) => {
  const {
    register,
    setValue,
    getValues,
    watch,
    reset,
    trigger,
    control,
    formState: { errors },
  } = useFormContext<NftFormFields>()

  const startDate = watch('startDate')
  const endDate = watch('endDate')

  const { getNumberOfBlocks, chainState } = useNftFormUtils()
  const { convertToUSD } = useTokenPrice()

  const numberOfBlocks = getNumberOfBlocks(startDate, endDate) || 0

  const totalDaysAndHours = getTotalDaysAndHours(startDate, endDate)
  const isEnglishAuction = watch('type') === 'english'
  const buyNowPrice = watch('buyNowPrice')
  const startingPrice = watch('startingPrice')

  useEffect(() => {
    setTimeout(() => {
      setValue(
        'endDate',
        isEnglishAuction
          ? getValues('endDate') || {
              type: 'duration',
              durationDays: 1,
            }
          : null
      )
    }, 0)
  }, [getValues, isEnglishAuction, setValue])

  useEffect(() => {
    setValue('auctionDurationBlocks', numberOfBlocks || undefined)
  }, [numberOfBlocks, setValue])

  const toggleActiveInput = (event?: React.ChangeEvent<HTMLInputElement>) => {
    if (!event) {
      return
    }
    const { name } = event.target
    setActiveInputs((prevState) => {
      if (!prevState.includes(name)) {
        if (name === 'buyNowPrice') {
          setValue('buyNowPrice', 2)
          trigger() // trigger form validation to make sure starting price is valid
        }
        return [...prevState, name]
      }
      if (name === 'whitelistedMembers') {
        setValue('whitelistedMembers', [])
      }
      if (name === 'auctionDuration') {
        setValue('startDate', null)
        setValue('endDate', null)
      } else if (name === 'startingPrice') {
        setValue('startingPrice', chainState.nftMinStartingPrice || undefined)
      } else {
        reset({ ...getValues(), [name]: undefined })
      }
      return prevState.filter((inputName) => inputName !== name)
    })
  }

  const headerText = {
    'Auction': {
      header: 'Auction',
      caption: 'Pick a timed or open auction. Optionally set up a buy now price.',
    },
    'Fixed price': {
      header: 'Fixed price',
      caption: 'Set up the price for your NFT. It can be changed later. Cancel anytime.',
    },
  }

  const days = [1, 3, 5, 7] as const

  const expirationDateItems = days.map((value) => ({
    name: pluralizeNoun(value, 'day'),
    value: {
      type: 'duration' as const,
      durationDays: value,
    },
  }))

  const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault()
    handleGoForward()
  }

  const handleNumberInputBlur = (event: React.FocusEvent<HTMLInputElement>) => {
    const { target } = event
    if (Number(target.value) % 1 !== 0) {
      setValue(target.name as 'buyNowPrice' | 'startingPrice', Math.floor(Number(event.target.value)))
    }
  }

  return (
    <>
      <Header variant="h500">{selectedType && headerText[selectedType].header}</Header>
      <Text variant="t300" secondary>
        {selectedType && headerText[selectedType].caption}
      </Text>
      <form onSubmit={handleSubmit}>
        {selectedType === 'Fixed price' && (
          <StyledFormField title="">
            <TextField
              {...register('buyNowPrice', { valueAsNumber: true })}
              type="number"
              nodeStart={<JoyTokenIcon variant="gray" size={24} />}
              nodeEnd={!!buyNowPrice && <Pill variant="overlay" label={`${convertToUSD(buyNowPrice)}`} />}
              error={!!errors.buyNowPrice}
              helperText={errors.buyNowPrice?.message}
              onBlur={handleNumberInputBlur}
            />
          </StyledFormField>
        )}
        {selectedType === 'Auction' && (
          <>
            <Controller
              name="type"
              control={control}
              defaultValue="open"
              render={({ field: { value, onChange } }) => (
                <OptionCardRadioWrapper>
                  <OptionCardRadio
                    value="open"
                    label="Open auction"
                    helperText="Pick the winning bid or cancel anytime"
                    onChange={() => onChange('open')}
                    selectedValue={value}
                  />
                  <OptionCardRadio
                    value="english"
                    label="Timed auction"
                    helperText="Highest bidder wins, cannot be cancelled once started"
                    onChange={() => onChange('english')}
                    selectedValue={value}
                  />
                </OptionCardRadioWrapper>
              )}
            />
            <AuctionDatePickerWrapper columns={isEnglishAuction ? 2 : 1}>
              <Controller
                name="startDate"
                control={control}
                render={({ field: { onChange, value }, fieldState: { error } }) => (
                  <AuctionDatePicker
                    size="regular"
                    label="Starts"
                    error={!!error}
                    helperText={error?.message}
                    minDate={new Date()}
                    maxDate={endDate?.type === 'date' && endDate.date < maxStartDate ? endDate.date : maxStartDate}
                    items={[
                      {
                        value: null,
                        name: 'Now',
                      },
                    ]}
                    onChange={onChange}
                    value={value}
                  />
                )}
              />
              {isEnglishAuction && (
                <Controller
                  name="endDate"
                  control={control}
                  render={({ field: { onChange, value }, fieldState: { error } }) => (
                    <AuctionDatePicker
                      size="regular"
                      label="Ends"
                      error={!!error}
                      helperText={error?.message}
                      minDate={(startDate?.type === 'date' && startDate.date) || new Date()}
                      maxDate={maxEndDate}
                      onChange={onChange}
                      items={expirationDateItems}
                      value={
                        value || {
                          type: 'duration',
                          durationDays: 1,
                        }
                      }
                    />
                  )}
                />
              )}
            </AuctionDatePickerWrapper>
            {numberOfBlocks > 0 && (
              <DaysSummary>
                <Text variant="t200-strong" color={cVar('colorTextMuted', true)}>
                  Total:
                </Text>
                &nbsp;
                <Text variant="t200-strong">{totalDaysAndHours}</Text>
                &nbsp;
                <Text variant="t200-strong" secondary>
                  / {formatNumber(numberOfBlocks)} blocks
                </Text>
                <DaysSummaryInfo text="On blockchain, duration is expressed in number of blocks" placement="top" />
              </DaysSummary>
            )}
            <FormField
              title="Minimum bid"
              switchProps={{
                name: 'startingPrice',
                onChange: toggleActiveInput,
                value: activeInputs.includes('startingPrice'),
              }}
              infoTooltip={{ text: 'Only bids higher than this value will be accepted' }}
            >
              <TextField
                {...register('startingPrice', { valueAsNumber: true })}
                type="number"
                defaultValue={chainState.nftMinStartingPrice?.toString()}
                nodeStart={<JoyTokenIcon variant="gray" size={24} />}
                nodeEnd={!!startingPrice && <Pill variant="overlay" label={`${convertToUSD(startingPrice)}`} />}
                disabled={!activeInputs.includes('startingPrice')}
                error={!!errors.startingPrice}
                helperText={errors.startingPrice?.message}
                onBlur={handleNumberInputBlur}
              />
            </FormField>
            <FormField
              title="Buy now price"
              switchProps={{
                name: 'buyNowPrice',
                onChange: toggleActiveInput,
                value: activeInputs.includes('buyNowPrice'),
              }}
              infoTooltip={{
                text: 'Bids matching this value will automatically end your auction',
              }}
            >
              <TextField
                {...register('buyNowPrice', { valueAsNumber: true })}
                placeholder="—"
                type="number"
                nodeStart={<JoyTokenIcon variant="gray" size={24} />}
                nodeEnd={!!buyNowPrice && <Pill variant="overlay" label={`${convertToUSD(buyNowPrice)}`} />}
                disabled={!activeInputs.includes('buyNowPrice')}
                error={!!errors.buyNowPrice}
                helperText={errors.buyNowPrice?.message}
                onBlur={(event) => {
                  trigger() // trigger form validation to make sure starting price is valid
                  handleNumberInputBlur(event)
                }}
              />
            </FormField>
            <FormField
              title="Whitelist"
              switchProps={{
                name: 'whitelistedMembers',
                onChange: toggleActiveInput,
                value: activeInputs.includes('whitelistedMembers'),
              }}
              infoTooltip={{
                text: 'Only members included in the whitelist will be able to bid on this auction',
              }}
            >
              <Controller
                name="whitelistedMembers"
                control={control}
                render={({ field: { onChange, value: existingMembers }, fieldState: { error } }) => {
                  return (
                    <MemberComboBox
                      disabled={!activeInputs.includes('whitelistedMembers')}
                      selectedMembers={existingMembers || []}
                      error={!!error}
                      helperText={error?.message}
                      onSelectMember={(member) => onChange([member, ...(existingMembers ? existingMembers : [])])}
                      onRemoveMember={(memberId) =>
                        onChange(existingMembers?.filter((existingMember) => existingMember.id !== memberId))
                      }
                    />
                  )
                }}
              />
            </FormField>
          </>
        )}
      </form>
    </>
  )
}
Example #27
Source File: [[...id]].tsx    From tams-club-cal with MIT License 4 votes vote down vote up
EditVolunteering = ({ volunteering, id, error }: InferGetServerSidePropsType<typeof getServerSideProps>) => {
    const router = useRouter();
    const [backdrop, setBackdrop] = useState(false);
    const [popupEvent, setPopupEvent] = useState<PopupEvent>(null);
    const { handleSubmit, control, setValue } = useForm();

    // When the user submits the form, either create or update the volunteering opportunity
    const onSubmit = async (data) => {
        // If the name is empty, do nothing
        if (!('name' in data)) return;

        // Create the volunteering object from the data
        const newVolunteering = createVolunteering(
            id,
            data.name,
            data.club,
            data.description,
            createFilters(data.limited, data.semester, data.setTimes, data.weekly, data.open),
            volunteering.history
        );

        // Start the upload process
        setBackdrop(true);

        // If the ID is null, create the volunteering, otherwise update it
        const res = id === null ? await postVolunteering(newVolunteering) : await putVolunteering(newVolunteering, id);

        // Finished uploading
        setBackdrop(false);

        // If the request was successful, redirect to the volunteering page, otherwise display an error
        if (res.status === 204) {
            new Cookies().set('success', id ? 'update-volunteering' : 'add-volunteering', {
                sameSite: 'strict',
                path: '/',
            });
            back();
        } else setPopupEvent(createPopupEvent('Unable to upload data. Please refresh the page or try again.', 4));
    };

    // Returns the user back to the volunteering display page
    const back = () => {
        router.push(`/volunteering${id ? `/${id}` : ''}`);
    };

    // On load
    useEffect(() => {
        // Send error if can't fetch resource
        if (error) {
            setPopupEvent(
                createPopupEvent('Error fetching volunteering info. Please refresh the page or add a new event.', 4)
            );
        }

        // When the volunteering data is loaded, set the open value to the controller as a seperate variable
        setValue('open', volunteering.filters.open);
    }, []);

    return (
        <EditWrapper>
            <TitleMeta title={`${id ? 'Edit' : 'Add'} Volunteering`} path={`/edit/volunteering/${id ? id : ''}`} />
            <UploadBackdrop open={backdrop} />
            <Popup event={popupEvent} />
            <Typography variant="h1" sx={{ textAlign: 'center', fontSize: '3rem' }}>
                {id ? 'Edit Volunteering' : 'Add Volunteering'}
            </Typography>
            {id ? <AddButton editHistory path={`/edit/history/volunteering/${id}`} /> : null}
            <FormWrapper onSubmit={handleSubmit(onSubmit)}>
                {/* TODO: Make a BoxWrapper component as this css is repeated so much across all forms */}
                <Box sx={{ marginBottom: 3, display: 'flex', flexDirection: { lg: 'row', xs: 'column' } }}>
                    <Controller
                        control={control}
                        name="open"
                        render={({ field: { onChange, onBlur, value } }) => (
                            <FormControlLabel
                                label="Open"
                                labelPlacement="start"
                                control={
                                    <Switch
                                        color="primary"
                                        value={value}
                                        onChange={onChange}
                                        onBlur={onBlur}
                                        checked={value}
                                        defaultChecked={volunteering.filters.open}
                                    />
                                }
                            />
                        )}
                    />
                    <Spacer />
                    <ControlledTextField
                        control={control}
                        setValue={setValue}
                        value={volunteering.name}
                        label="Volunteering Name"
                        name="name"
                        variant="outlined"
                        grow
                        required
                        errorMessage="Please enter a name"
                        sx={{ marginLeft: { lg: 2, xs: 0 } }}
                    />
                    <Spacer />
                    <ControlledTextField
                        control={control}
                        setValue={setValue}
                        value={volunteering.club}
                        label="Club"
                        name="club"
                        variant="outlined"
                        grow
                        required
                        errorMessage="Please enter a club name"
                    />
                </Box>
                <Hidden mdDown>
                    <Typography sx={{ display: 'inline', marginRight: { lg: 16, xs: 0 } }}>Filters:</Typography>
                </Hidden>
                <ControlledFilterCheckbox
                    control={control}
                    setValue={setValue}
                    name="limited"
                    label="Limited Slots"
                    value={volunteering.filters.limited}
                />
                <ControlledFilterCheckbox
                    control={control}
                    setValue={setValue}
                    name="semester"
                    label="Semester Long Committment"
                    value={volunteering.filters.semester}
                />
                <ControlledFilterCheckbox
                    control={control}
                    setValue={setValue}
                    name="setTimes"
                    label="Set Time Slots"
                    value={volunteering.filters.setTimes}
                />
                <ControlledFilterCheckbox
                    control={control}
                    setValue={setValue}
                    name="weekly"
                    label="Repeats Weekly"
                    value={volunteering.filters.weekly}
                />
                <ControlledTextField
                    control={control}
                    setValue={setValue}
                    value={volunteering.description}
                    label="Description (optional)"
                    name="description"
                    variant="outlined"
                    area
                    sx={{ marginTop: 2 }}
                />
                <TwoButtonBox success="Submit" onCancel={back} onSuccess={onSubmit} submit right />
            </FormWrapper>
        </EditWrapper>
    );
}
Example #28
Source File: LoginView.tsx    From storefront with MIT License 4 votes vote down vote up
LoginView: React.VFC = () => {
  const [alert, setAlert] = useState<string>();
  const { closeModal } = useUI();
  const [login, { loading }] = useLoginMutation();

  const { control, formState, handleSubmit } = useForm<LoginMutationVariables>({
    resolver: yupResolver(validationSchema),
  });

  const onSubmit = handleSubmit(async (variables) => {
    try {
      const { data } = await login({ variables });

      if (data?.login?.authToken != null) {
        localStorage.setItem('authToken', data.login.authToken);
      }
      if (data?.login?.refreshToken != null) {
        localStorage.setItem('refreshToken', data.login.refreshToken);
      }
      closeModal();
    } catch (error) {
      if (isApolloError(error)) {
        setAlert(error.graphQLErrors[0].message);
      }
    }
  });

  return (
    <>
      <Box sx={{ textAlign: 'center', mb: 6 }}>
        <Logo />
      </Box>
      <form onSubmit={onSubmit}>
        {alert && (
          <Box sx={{ mb: 2 }}>
            <Alert severity="error">{alert}</Alert>
          </Box>
        )}
        {/* Username or email */}
        <Controller
          control={control}
          name="username"
          render={({ field }) => (
            <TextField
              required
              autoCapitalize="off"
              autoCorrect="off"
              error={'username' in formState.errors}
              helperText={formState.errors.username?.message}
              label="Username or email"
              type="text"
              {...field}
            />
          )}
        />
        {/* Password */}
        <Controller
          control={control}
          name="password"
          render={({ field }) => (
            <TextField
              required
              error={'password' in formState.errors}
              helperText={formState.errors.password?.message}
              label="Password"
              type="password"
              {...field}
            />
          )}
        />
        <Box sx={{ mt: 1 }}>
          <Button fullWidth type="submit" color="primary" loading={loading}>
            Log in
          </Button>
        </Box>
        <Box sx={{ mt: 1, textAlign: 'center' }}>
          Don’t have an account? <Link href="/register">Register</Link>
        </Box>
      </form>
    </>
  );
}
Example #29
Source File: ShortcutForm.tsx    From backstage with Apache License 2.0 4 votes vote down vote up
ShortcutForm = ({ formValues, onSave, onClose }: Props) => {
  const classes = useStyles();

  const {
    handleSubmit,
    reset,
    control,
    formState: { errors },
  } = useForm<FormValues>({
    mode: 'onChange',
    defaultValues: {
      url: formValues?.url ?? '',
      title: formValues?.title ?? '',
    },
  });

  useEffect(() => {
    reset(formValues);
  }, [reset, formValues]);

  return (
    <>
      <CardContent>
        <Controller
          name="url"
          control={control}
          rules={{
            required: true,
            pattern: {
              value: /^\//,
              message: 'Must be a relative URL (starts with a /)',
            },
          }}
          render={({ field }) => (
            <TextField
              {...field}
              error={!!errors.url}
              helperText={errors.url?.message}
              type="text"
              placeholder="Enter a URL"
              InputLabelProps={{
                shrink: true,
              }}
              className={classes.field}
              fullWidth
              label="Shortcut URL"
              variant="outlined"
              autoComplete="off"
            />
          )}
        />
        <Controller
          name="title"
          control={control}
          rules={{
            required: true,
            minLength: {
              value: 2,
              message: 'Must be at least 2 characters',
            },
          }}
          render={({ field }) => (
            <TextField
              {...field}
              error={!!errors.title}
              helperText={errors.title?.message}
              type="text"
              placeholder="Enter a display name"
              InputLabelProps={{
                shrink: true,
              }}
              className={classes.field}
              fullWidth
              label="Display Name"
              variant="outlined"
              autoComplete="off"
            />
          )}
        />
      </CardContent>
      <CardActions classes={{ root: classes.actionRoot }}>
        <Button
          variant="contained"
          color="primary"
          size="large"
          onClick={handleSubmit(onSave)}
        >
          Save
        </Button>
        <Button variant="outlined" size="large" onClick={onClose}>
          Cancel
        </Button>
      </CardActions>
    </>
  );
}