formik#getIn TypeScript Examples

The following examples show how to use formik#getIn. 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: useForm.tsx    From SQForm with MIT License 6 votes vote down vote up
function _getHasValue(meta: FieldMetaProps<unknown>) {
  const fieldValue = getIn(meta, 'value');

  if (Array.isArray(fieldValue)) {
    return !!fieldValue.length;
  }

  if (typeof fieldValue === 'number') {
    return true;
  }

  if (typeof fieldValue === 'boolean') {
    return true;
  }

  return !!fieldValue;
}
Example #2
Source File: useForm.tsx    From SQForm with MIT License 6 votes vote down vote up
function _transformErrorMessageToString<TValue>(meta: FieldMetaProps<TValue>) {
  const error = getIn(meta, 'error');

  if (Array.isArray(error)) {
    return error.join('').toLowerCase() || undefined;
  }

  return error;
}
Example #3
Source File: PhoneInput.tsx    From glific-frontend with GNU Affero General Public License v3.0 6 votes vote down vote up
PhoneInput: React.SFC<InputProps> = ({
  enableSearch = true,
  form: { errors, setFieldValue },
  field,
  inputProps = {
    name: field.name,
    required: true,
    autoFocus: false,
  },
  ...props
}) => {
  const errorText = getIn(errors, field.name);
  const { placeholder } = props;

  return (
    <div className={styles.Input} data-testid="phoneInput">
      <FormControl>
        <ReactPhoneInput
          containerClass={styles.Container}
          inputClass={styles.PhoneNumber}
          data-testid="phoneNumber"
          placeholder={placeholder}
          enableSearch={enableSearch}
          country="in"
          autoFormat={false}
          inputProps={inputProps}
          {...field}
          value={field.value}
          onChange={(event) => {
            setFieldValue(field.name, event);
          }}
        />
        {errorText ? (
          <FormHelperText classes={{ root: styles.FormHelperText }}>{errorText}</FormHelperText>
        ) : null}
      </FormControl>
    </div>
  );
}
Example #4
Source File: InputChip.tsx    From frontegg-react with MIT License 6 votes vote down vote up
FInputChip: FC<InputChipProps & { name: string }> = ({ name, disabled, onChange, ...props }) => {
  const [inputProps, { touched, error }, { setValue, setTouched }] = useField(name);
  const { values, isSubmitting, validateForm } = useFormikContext();

  const debounceError = useDebounce(error, 2000);

  useEffect(() => {
    !!debounceError && validateForm(values);
  }, [validateForm, debounceError, values]);

  const onValidate = useCallback(
    async (value: string[]) => {
      !touched && setTouched(true);
      const errors = await validateForm(setIn(values, name, value));
      return !getIn(errors, name);
    },
    [setTouched, name, values, validateForm]
  );

  return (
    <InputChip
      {...inputProps}
      {...props}
      validate={onValidate}
      disabled={isSubmitting || disabled}
      error={touched && error ? error : undefined}
      onChange={onChange ?? ((val) => setValue(val))}
    />
  );
}
Example #5
Source File: TextArrayField.tsx    From netify with BSD 2-Clause "Simplified" License 5 votes vote down vote up
TextArrayField = memo<TextArrayFieldProps>(function TextArrayField(props) {
	const {name, placeholder, addControlTitle = 'Add new one', removeControlTitle = 'Remove item'} = props;

	return (
		<ul className={styles.root}>
			<FieldArray
				name={name}
				render={helpers => {
					const list = getIn(helpers.form.values, name);
					return list.map((_: any, index: number) => (
						// eslint-disable-next-line react/no-array-index-key
						<li key={index} className={styles.item}>
							<div className={styles.entry}>
								<TextField
									className={styles.field}
									name={`${name}[${index}]`}
									placeholder={placeholder}
								/>

								{index === list.length - 1 ? (
									<IconButton
										className={styles.control}
										icon={<AddIcon />}
										tooltip={addControlTitle}
										onClick={() => helpers.push('')}
									/>
								) : (
									<IconButton
										className={styles.control}
										icon={<RemoveIcon />}
										tooltip={removeControlTitle}
										onClick={() => helpers.remove(index)}
									/>
								)}
							</div>
							<FieldError name={`${name}[${index}]`} />
						</li>
					));
				}}
			/>
		</ul>
	);
})
Example #6
Source File: KeyValueArrayField.tsx    From netify with BSD 2-Clause "Simplified" License 5 votes vote down vote up
KeyValueArrayField = memo<KeyValueArrayFieldProps>(function KeyValueArrayField(props) {
	const {
		name,
		keyNameSuffix,
		valueNameSuffix,
		keyPlaceholder,
		valuePlaceholder,
		addControlTitle = 'Add new one',
		removeControlTitle = 'Remove item',
	} = props;

	return (
		<ul className={styles.root}>
			<FieldArray
				name={name}
				render={helpers => {
					const list = getIn(helpers.form.values, name);
					return list.map((_: any, index: number) => (
						// eslint-disable-next-line react/no-array-index-key
						<li key={index} className={styles.item}>
							<div className={styles.entry}>
								<TextField
									className={styles.field}
									name={`${name}[${index}].${keyNameSuffix}`}
									placeholder={keyPlaceholder}
								/>

								<TextField
									className={styles.field}
									name={`${name}[${index}].${valueNameSuffix}`}
									placeholder={valuePlaceholder}
								/>

								{index === list.length - 1 ? (
									<IconButton
										className={styles.control}
										icon={<AddIcon />}
										tooltip={addControlTitle}
										onClick={() => helpers.push({[keyNameSuffix]: '', [valueNameSuffix]: ''})}
									/>
								) : (
									<IconButton
										className={styles.control}
										icon={<RemoveIcon />}
										tooltip={removeControlTitle}
										onClick={() => helpers.remove(index)}
									/>
								)}
							</div>
							<FieldError name={`${name}[${index}].${keyNameSuffix}`} />
							<FieldError name={`${name}[${index}].${valueNameSuffix}`} />
						</li>
					));
				}}
			/>
		</ul>
	);
})
Example #7
Source File: TimePicker.tsx    From glific-frontend with GNU Affero General Public License v3.0 5 votes vote down vote up
TimePicker: React.SFC<TimePickerProps> = ({
  variant = 'inline',
  inputVariant = 'outlined',
  field,
  form: { setFieldValue, touched, errors },
  placeholder,
  disabled = false,
  helperText,
}) => {
  moment.defaultFormat = 'Thh:mm:ss';
  const dateValue = field.value ? moment(field.value, moment.defaultFormat).toDate() : null;
  const [open, setOpen] = useState(false);

  const errorText = getIn(errors, field.name);
  const touchedVal = getIn(touched, field.name);
  const hasError = touchedVal && errorText !== undefined;

  const handleDateChange = (time: Date | null) => {
    const value = time ? moment(time).format('THH:mm:ss') : null;
    setFieldValue(field.name, value);
  };

  return (
    <MuiPickersUtilsProvider utils={DateFnsUtils}>
      <Grid className={styles.TimePicker}>
        <KeyboardTimePicker
          error={hasError}
          autoOk
          open={open}
          variant={variant}
          inputVariant={inputVariant}
          label={placeholder}
          data-testid="time-picker"
          mask="__:__ _M"
          value={dateValue}
          onClick={() => !disabled && setOpen(true)}
          onClose={() => setOpen(false)}
          disabled={disabled}
          onChange={(date) => handleDateChange(date)}
          keyboardIcon={<ScheduleIcon />}
          helperText={hasError ? errorText : ''}
          className={styles.picker}
        />
        {helperText && (
          <div id="helper-text" className={styles.HelperText}>
            {helperText}
          </div>
        )}
      </Grid>
    </MuiPickersUtilsProvider>
  );
}
Example #8
Source File: OptionSet.tsx    From amplication with Apache License 2.0 5 votes vote down vote up
OptionSetOptions = ({
  form,
  name,
  remove,
  replace,
  label,
}: {
  label: string;
} & FieldArrayRenderProps) => {
  const value = get(form.values, name) || [];
  const [push, hasNew] = useVirtualPush(value);

  const errors = useMemo(() => {
    const error = getIn(form.errors, name);
    if (typeof error === "string") return error;
    return null;
  }, [form.errors, name]);

  const options = hasNew ? [...value, {}] : value;

  return (
    <div>
      <h3>{label}</h3>
      {errors && <div className="option-set__error-message">{errors}</div>}
      {options.map((option: OptionItem, index: number) => (
        <OptionSetOption
          key={index}
          index={index}
          onChange={replace}
          onRemove={remove}
          name={name}
        />
      ))}
      <Button onClick={push} buttonStyle={EnumButtonStyle.Clear}>
        <Icon icon="plus" />
        Add option
      </Button>
    </div>
  );
}
Example #9
Source File: DateTimePicker.tsx    From glific-frontend with GNU Affero General Public License v3.0 5 votes vote down vote up
DateTimePicker: React.SFC<DateTimePickerProps> = ({
  variant = 'inline',
  inputVariant = 'outlined',
  format = 'dd/MM/yyyy hh:mm a',
  field,
  form: { touched, errors, setFieldValue },
  placeholder,
  minDate,
  onChange,
}) => {
  const errorText = getIn(errors, field.name);
  const touchedVal = getIn(touched, field.name);
  const hasError = touchedVal && errorText !== undefined;
  const dateValue = field.value ? field.value : null;

  const handleDateChange = (date: Date | null | string) => {
    const value = date && date.toString() !== 'Invalid Date' ? date : null;
    setFieldValue(field.name, value);
    if (onChange) onChange(value);
  };

  const icon = <CalenderIcon />;
  return (
    <MuiPickersUtilsProvider utils={DateFnsUtils}>
      <Grid className={styles.DateTimePicker}>
        <KeyboardDateTimePicker
          className={styles.Text}
          error={hasError}
          autoOk
          variant={variant}
          inputVariant={inputVariant}
          format={format}
          data-testid="date-picker-inline"
          label={placeholder}
          value={dateValue}
          onChange={handleDateChange}
          helperText={hasError ? errorText : ''}
          minDate={minDate}
          keyboardIcon={icon}
        />
      </Grid>
    </MuiPickersUtilsProvider>
  );
}
Example #10
Source File: Calendar.tsx    From glific-frontend with GNU Affero General Public License v3.0 5 votes vote down vote up
Calendar: React.SFC<CalendarProps> = ({
  variant = 'inline',
  inputVariant = 'outlined',
  format = 'MM/dd/yyyy',
  field,
  disabled = false,
  form: { touched, errors, setFieldValue },
  placeholder,
  minDate,
}) => {
  const errorText = getIn(errors, field.name);
  const touchedVal = getIn(touched, field.name);
  const hasError = touchedVal && errorText !== undefined;
  const dateValue = field.value ? field.value : null;
  const [open, setOpen] = useState(false);

  const handleDateChange = (date: Date | null | string) => {
    if (date) {
      if (date !== 'Invalid Date') setFieldValue(field.name, date);
    } else {
      setFieldValue(field.name, null);
    }
  };

  return (
    <MuiPickersUtilsProvider utils={DateFnsUtils}>
      <Grid className={styles.Calendar}>
        <KeyboardDatePicker
          error={hasError}
          autoOk
          open={open}
          variant={variant}
          inputVariant={inputVariant}
          format={format}
          className={styles.CalendarInput}
          disabled={disabled}
          data-testid="date-picker-inline"
          label={placeholder}
          value={dateValue}
          onClick={() => !disabled && setOpen(true)}
          onClose={() => setOpen(false)}
          onChange={handleDateChange}
          helperText={hasError ? errorText : ''}
          minDate={minDate}
        />
      </Grid>
    </MuiPickersUtilsProvider>
  );
}
Example #11
Source File: Governance.tsx    From homebase-app with MIT License 4 votes vote down vote up
GovernanceForm = ({
  submitForm,
  values,
  setFieldValue,
  errors,
  touched,
}: any) => {
  const { network } = useTezos();
  const {
    dispatch,
    state: {
      data: { orgSettings },
    },
  } = useContext(CreatorContext);
  const match = useRouteMatch();
  const history = useHistory();
  const [blockTimeAverage, setBlockTimeAverage] = useState<number>(0);
  const { votingBlocks, proposalFlushBlocks, proposalExpiryBlocks } = values;
  const {
    creationMoment,
    closeMoment,
    flushMoment,
    expiryMoment,
    votingTime,
    flushDelayTime,
    activeMoment,
    expiryDelayTime,
  } = useEstimatedBlockTimes({
    votingBlocks,
    proposalFlushBlocks,
    proposalExpiryBlocks,
    blockTimeAverage,
  });

  useEffect(() => {
    (async () => {
      const blockchainInfo = await getNetworkStats(network);
      if (blockchainInfo) {
        setBlockTimeAverage(blockchainInfo.minimal_block_delay);
      }
    })();
  }, [network]);

  useEffect(() => {
    if (values) {
      dispatch({
        type: ActionTypes.UPDATE_NAVIGATION_BAR,
        next: {
          text: "CONTINUE",
          handler: () => {
            submitForm(values);
          },
        },
        back: {
          text: "BACK",
          handler: () => history.push(`dao`),
        },
      });
    }
  }, [dispatch, errors, history, match.path, match.url, submitForm, values]);

  return (
    <>
      <Grid container>
        <Grid item style={{ marginRight: 15 }}>
          <SecondContainer container direction="row">
            <Typography
              style={styles.voting}
              variant="subtitle1"
              color="textSecondary"
            >
              Voting Cycle Duration
            </Typography>
          </SecondContainer>

          <GridItemContainer>
            <CustomInputContainer item xs={12}>
              <ItemContainer
                container
                direction="row"
                alignItems="center"
                justify="center"
              >
                <GridItemCenter item xs={6}>
                  <Field
                    name="votingBlocks"
                    type="number"
                    placeholder="00"
                    component={TextField}
                    inputProps={{ min: 0 }}
                  />
                </GridItemCenter>
                <GridItemCenter item xs={6}>
                  <Typography color="textSecondary">blocks</Typography>
                </GridItemCenter>
              </ItemContainer>
            </CustomInputContainer>
          </GridItemContainer>

          <Grid item>
            {errors.votingBlocks && touched.votingBlocks ? (
              <ErrorText>{errors.votingBlocks}</ErrorText>
            ) : null}
          </Grid>

          <Grid item style={{ margin: "14px 15px", height: 62 }}>
            <EstimatedTime {...votingTime} />
          </Grid>
        </Grid>
        <Grid item style={{ marginRight: 15 }}>
          <SecondContainer container direction="row">
            <Typography
              style={styles.voting}
              variant="subtitle1"
              color="textSecondary"
            >
              Proposal Execution Delay
            </Typography>
          </SecondContainer>

          <GridItemContainer>
            <CustomInputContainer item xs={12}>
              <ItemContainer
                container
                direction="row"
                alignItems="center"
                justify="center"
              >
                <GridItemCenter item xs={6}>
                  <Field
                    name="proposalFlushBlocks"
                    type="number"
                    placeholder="00"
                    component={TextField}
                    inputProps={{ min: 0 }}
                  />
                </GridItemCenter>
                <GridItemCenter item xs={6}>
                  <Typography color="textSecondary">blocks</Typography>
                </GridItemCenter>
              </ItemContainer>
            </CustomInputContainer>
          </GridItemContainer>

          <Grid item>
            {errors.proposalFlushBlocks && touched.proposalFlushBlocks ? (
              <ErrorText>{errors.proposalFlushBlocks}</ErrorText>
            ) : null}
          </Grid>

          <Grid item style={{ marginLeft: 15, height: 62, marginTop: 14 }}>
            <EstimatedTime {...flushDelayTime} />
          </Grid>
        </Grid>

        <Grid item style={{ marginRight: 15 }}>
          <SecondContainer container direction="row">
            <Typography
              style={styles.voting}
              variant="subtitle1"
              color="textSecondary"
            >
              Proposal Expiration Threshold
            </Typography>
          </SecondContainer>

          <GridItemContainer>
            <CustomInputContainer item xs={12}>
              <ItemContainer
                container
                direction="row"
                alignItems="center"
                justify="center"
              >
                <GridItemCenter item xs={6}>
                  <Field
                    name="proposalExpiryBlocks"
                    type="number"
                    placeholder="00"
                    component={TextField}
                    inputProps={{ min: 0 }}
                  />
                </GridItemCenter>
                <GridItemCenter item xs={6}>
                  <Typography color="textSecondary">blocks</Typography>
                </GridItemCenter>
              </ItemContainer>
            </CustomInputContainer>
          </GridItemContainer>

          <Grid item>
            {errors.proposalExpiryBlocks && touched.proposalExpiryBlocks ? (
              <ErrorText>{errors.proposalExpiryBlocks}</ErrorText>
            ) : null}
          </Grid>

          <Grid item style={{ marginLeft: 15, height: 62, marginTop: 14 }}>
            <EstimatedTime {...expiryDelayTime} />
          </Grid>
        </Grid>
      </Grid>

      <Grid item style={{ margin: "24px 0" }}>
        <Typography color={"textSecondary"}>
          If Jane creates a DAO at{" "}
          <CustomSpan>{dayjs().format("HH:mm MM/DD")}</CustomSpan>, she will be
          able to create a proposal at{" "}
          <CustomSpan>{creationMoment.format("HH:mm MM/DD")}</CustomSpan>, and
          the DAO will vote on it from{" "}
          <CustomSpan>{activeMoment.format("HH:mm MM/DD")} </CustomSpan>
          through <CustomSpan>{closeMoment.format("HH:mm MM/DD")}</CustomSpan>.
          If the proposal passes, it&apos;ll be executable at{" "}
          <CustomSpan>{flushMoment.format("HH:mm MM/DD")}</CustomSpan> and will
          expire at{" "}
          <CustomSpan>{expiryMoment.format("HH:mm MM/DD")}</CustomSpan>
        </Typography>
      </Grid>

      <Grid item style={{ marginTop: 12 }}>
        <SecondContainer container direction="row">
          <Typography
            style={styles.voting}
            variant="subtitle1"
            color="textSecondary"
          >
            Required Stake to Propose
          </Typography>
        </SecondContainer>

        <StakeContainer container direction="row" alignItems="center">
          <AdditionContainer item xs={11} sm={4}>
            <ItemContainer
              container
              direction="row"
              alignItems="center"
              justify="center"
            >
              <GridItemCenter item xs={6}>
                <Field
                  name="proposeStakeRequired"
                  type="number"
                  placeholder="00"
                  inputProps={{ min: 0, defaultValue: 0 }}
                  component={TextField}
                />
              </GridItemCenter>
              <GridItemCenter
                item
                xs={6}
                container
                direction="row"
                justify="space-around"
              >
                <Typography color="textSecondary">
                  {orgSettings.governanceToken.tokenMetadata?.symbol || ""}
                </Typography>
                <Tooltip
                  placement="bottom"
                  title={`Amount of ${
                    orgSettings.governanceToken.tokenMetadata?.symbol || ""
                  } required to make a proposal. Total supply: ${
                    orgSettings.governanceToken.tokenMetadata?.supply
                  }`}
                >
                  <InfoIconInput color="secondary" />
                </Tooltip>
              </GridItemCenter>
            </ItemContainer>
          </AdditionContainer>
          {errors.proposeStakeRequired || errors.proposeStakePercentage ? (
            <ErrorText>
              {errors.proposeStakeRequired || errors.proposeStakePercentage}
            </ErrorText>
          ) : null}
        </StakeContainer>
      </Grid>

      <SecondContainer container direction="row">
        <Typography
          style={styles.voting}
          variant="subtitle1"
          color="textSecondary"
        >
          Returned Stake After Proposal Rejection
        </Typography>

        <Grid
          container
          direction="row"
          alignItems="center"
          spacing={1}
          style={{ marginTop: 14 }}
        >
          <GridNoPadding item xs={8} sm={9}>
            <Field name="returnedTokenPercentage">
              {() => (
                <StyledSlider
                  value={getIn(values, "returnedTokenPercentage")}
                  onChange={(value: any, newValue: any) =>
                    setFieldValue("returnedTokenPercentage", newValue || 0)
                  }
                />
              )}
            </Field>
          </GridNoPadding>
          <GridNoPadding item xs={4} sm={3}>
            <CustomSliderValue>
              <Value variant="subtitle1" color="textSecondary">
                {getIn(values, "returnedTokenPercentage")}%
              </Value>
            </CustomSliderValue>
          </GridNoPadding>
        </Grid>
      </SecondContainer>

      <SpacingContainer direction="row" container alignItems="center">
        <Typography variant="subtitle1" color="textSecondary">
          Min & Max Transfer Amounts
        </Typography>
      </SpacingContainer>
      <Grid
        container
        direction="row"
        alignItems="center"
        style={{ marginTop: 14 }}
      >
        <AdditionContainer item xs={12} sm={4}>
          <ItemContainer
            container
            direction="row"
            alignItems="center"
            justify="center"
          >
            <GridItemCenter item xs={5}>
              <Field
                name="minXtzAmount"
                type="number"
                placeholder="00"
                component={TextField}
              />
            </GridItemCenter>
            <GridItemCenter
              item
              xs={7}
              container
              direction="row"
              justify="space-around"
            >
              <ValueText color="textSecondary">Min. XTZ</ValueText>
              <Tooltip
                placement="bottom"
                title="Minimum amount of XTZ that can be transferred"
              >
                <InfoIconInput color="secondary" />
              </Tooltip>
            </GridItemCenter>
          </ItemContainer>
          {errors.minXtzAmount && touched.minXtzAmount ? (
            <ErrorText>{errors.minXtzAmount}</ErrorText>
          ) : null}
        </AdditionContainer>
        <AdditionContainer item xs={12} sm={4}>
          <ItemContainer
            container
            direction="row"
            alignItems="center"
            justify="center"
          >
            <GridItemCenter item xs={5}>
              <Field
                name="maxXtzAmount"
                type="number"
                placeholder="00"
                component={TextField}
              />
            </GridItemCenter>
            <GridItemCenter
              item
              xs={7}
              container
              direction="row"
              justify="space-around"
            >
              <ValueText color="textSecondary">Max. XTZ </ValueText>
              <Tooltip
                placement="bottom"
                title="Maximum amount of XTZ that can be transferred"
              >
                <InfoIconInput color="secondary" />
              </Tooltip>
            </GridItemCenter>
          </ItemContainer>
          {errors.maxXtzAmount && touched.maxXtzAmount ? (
            <ErrorText>{errors.maxXtzAmount}</ErrorText>
          ) : null}
        </AdditionContainer>
      </Grid>
    </>
  );
}
Example #12
Source File: AutoComplete.tsx    From glific-frontend with GNU Affero General Public License v3.0 4 votes vote down vote up
AutoComplete: React.SFC<AutocompleteProps> = ({
  options,
  optionLabel,
  additionalOptionLabel,
  field,
  icon,
  chipIcon,
  form: { touched, errors, setFieldValue },
  textFieldProps,
  helperText,
  questionText,
  multiple = true,
  disabled = false,
  freeSolo = false,
  autoSelect = false,
  getOptions,
  asyncValues,
  roleSelection,
  onChange,
  asyncSearch = false,
  helpLink,
  noOptionsText = 'No options available',
  openOptions,
  disableClearable = false,
  listBoxProps,
  classes = {},
  renderTags = true,
  selectedOptionsIds = [],
  selectTextAsOption = false,
  onInputChange = () => null,
  valueElementName = 'id',
}) => {
  const errorText = getIn(errors, field.name);
  const touchedVal = getIn(touched, field.name);
  const hasError = touchedVal && errorText !== undefined;
  const [searchTerm, setSearchTerm] = useState('');
  const [optionValue, setOptionValue] = useState([]);
  const [open, setOpen] = useState(false);

  useEffect(() => {
    if (options.length > 0) {
      setOptionValue(options);
    }
  }, [options]);

  useEffect(() => {
    if (getOptions && getOptions()) {
      const optionList = getOptions();
      if (optionList.length > 0) setOptionValue(optionList);
    }
  }, [open, getOptions]);

  const getValue = (() => {
    if (multiple && asyncSearch) return asyncValues.value;
    if (multiple) {
      if (optionValue.length > 0 && field.value) {
        return optionValue.filter((option: any) =>
          field.value.map((value: any) => value.id).includes(option.id)
        );
      }
      return [];
    }
    return field.value;
  })();

  const getLabel = (option: any) => {
    if (option[optionLabel]) {
      return option[optionLabel];
    }
    if (additionalOptionLabel) {
      return option[additionalOptionLabel];
    }
    return option;
  };

  /**
   *
   * @param value Callback value
   * @param getTagProps Render tag props
   *
   */
  const getRenderTags = (value: Array<any>, getTagProps: any) => {
    let tagsToRender = value;

    /**
     * when renderTags is true,
     * default selected options along with newly selected options will be visible
     * else,
     * only post selected options will be visible

     */
    if (!renderTags) {
      tagsToRender = value.filter((option: any) => !selectedOptionsIds.includes(option.id));
    }

    return tagsToRender.map((option: any, index: number) => {
      const props = getTagProps({ index });

      /**
       * If disableClearable is true, removing onDelete event
       * deleteIcon component will be disabled, when onDelete is absent
       */
      if (disableClearable) {
        delete props.onDelete;
      }

      return (
        <Chip
          data-testid="searchChip"
          style={{ backgroundColor: '#e2f1ea' }}
          className={styles.Chip}
          icon={chipIcon}
          label={getLabel(option)}
          {...props}
          deleteIcon={<DeleteIcon className={styles.DeleteIcon} data-testid="deleteIcon" />}
        />
      );
    });
  };

  const getOptionDisabled = (option: any) => selectedOptionsIds.includes(option.id);

  return (
    <div className={styles.Input}>
      <FormControl fullWidth error={hasError}>
        {questionText ? <div className={styles.QuestionText}>{questionText}</div> : null}
        <Autocomplete
          classes={classes}
          multiple={multiple}
          data-testid="autocomplete-element"
          options={optionValue}
          freeSolo={freeSolo}
          autoSelect={autoSelect}
          disableClearable={disableClearable}
          getOptionLabel={(option: any) => (option[optionLabel] ? option[optionLabel] : option)}
          getOptionDisabled={getOptionDisabled}
          getOptionSelected={(option, value) => {
            if (value) {
              return option[valueElementName] === value[valueElementName];
            }
            return false;
          }}
          onChange={(event, value: any) => {
            if (roleSelection) {
              roleSelection(value);
            }
            if (asyncSearch && value.length > 0) {
              const filterValues = asyncValues.value.filter(
                (val: any) => val.id !== value[value.length - 1].id
              );
              if (filterValues.length === value.length - 2) {
                asyncValues.setValue(filterValues);
              } else {
                asyncValues.setValue([...value]);
              }
              setSearchTerm('');
              onChange('');
            } else if (asyncSearch && value.length === 0) {
              asyncValues.setValue([]);
            }
            if (onChange) {
              onChange(value);
            }
            setFieldValue(field.name, value);
          }}
          onInputChange={onInputChange}
          inputValue={asyncSearch ? searchTerm : undefined}
          value={getValue}
          disabled={disabled}
          disableCloseOnSelect={multiple}
          renderTags={getRenderTags}
          renderOption={(option: any, { selected }) => (
            <>
              {multiple ? (
                <Checkbox
                  icon={icon}
                  checked={
                    asyncSearch
                      ? asyncValues.value.map((value: any) => value.id).includes(option.id)
                      : selected
                  }
                  color="primary"
                />
              ) : (
                ''
              )}
              {getLabel(option)}
            </>
          )}
          renderInput={(params: any) => {
            const { inputProps } = params;
            inputProps.value = selectTextAsOption ? field.value : inputProps.value;
            const asyncChange = asyncSearch
              ? {
                  onChange: (event: any) => {
                    setSearchTerm(event.target.value);
                    return onChange(event.target.value);
                  },
                }
              : null;
            return (
              <TextField
                {...params}
                inputProps={inputProps}
                {...asyncChange}
                error={hasError}
                helperText={hasError ? errorText : ''}
                {...textFieldProps}
                data-testid="AutocompleteInput"
              />
            );
          }}
          open={openOptions || open}
          onOpen={() => {
            setOpen(true);
          }}
          onClose={() => {
            setOpen(false);
          }}
          noOptionsText={noOptionsText}
          ListboxProps={listBoxProps}
        />
        {helperText ? (
          <FormHelperText className={styles.HelperText}>{helperText}</FormHelperText>
        ) : null}

        {helpLink ? (
          <div
            className={styles.HelpLink}
            onKeyDown={() => helpLink.handleClick()}
            onClick={() => helpLink.handleClick()}
            role="button"
            data-testid="helpButton"
            tabIndex={0}
          >
            {helpLink.label}
          </div>
        ) : null}
      </FormControl>
    </div>
  );
}
Example #13
Source File: form_factory.tsx    From firecms with MIT License 4 votes vote down vote up
function FieldInternal<T extends CMSType, M extends { [Key: string]: any }>
({
     component,
     componentProps: {
         name,
         property,
         includeDescription,
         underlyingValueHasChanged,
         tableMode,
         partOfArray,
         autoFocus,
         context,
         disabled,
         shouldAlwaysRerender
     },
     fieldProps

 }:
     {
         component: ComponentType<FieldProps<T>>,
         componentProps: CMSFormFieldProps<M>,
         fieldProps: FormikFieldProps<T>
     }) {

    const customFieldProps: any = property.config?.customProps;
    const value = fieldProps.field.value;
    const initialValue = fieldProps.meta.initialValue;
    const error = getIn(fieldProps.form.errors, name);
    const touched = getIn(fieldProps.form.touched, name);

    const showError: boolean = error &&
        (fieldProps.form.submitCount > 0 || property.validation?.unique) &&
        (!Array.isArray(error) || !!error.filter((e: any) => !!e).length);

    const isSubmitting = fieldProps.form.isSubmitting;

    const [internalValue, setInternalValue] = useState<T | null>(value);
    useEffect(
        () => {
            const handler = setTimeout(() => {
                fieldProps.form.setFieldValue(name, internalValue);
            }, 50);

            return () => {
                clearTimeout(handler);
            };
        },
        [internalValue]
    );

    useEffect(
        () => {
            if (!isEqual(value, internalValue)) {
                setInternalValue(value);
            }
        },
        [value]
    );

    const cmsFieldProps: FieldProps<T> = {
        name,
        value: internalValue as T,
        initialValue,
        setValue: (value: T | null) => {
            fieldProps.form.setFieldTouched(name, true, false);
            setInternalValue(value);
        },
        error,
        touched,
        showError,
        isSubmitting,
        includeDescription: includeDescription ?? true,
        property: property as Property<T>,
        disabled: disabled ?? false,
        underlyingValueHasChanged: underlyingValueHasChanged ?? false,
        tableMode: tableMode ?? false,
        partOfArray: partOfArray ?? false,
        autoFocus: autoFocus ?? false,
        customProps: customFieldProps,
        context,
        shouldAlwaysRerender: shouldAlwaysRerender ?? true
    };

    return (
        <>

            {React.createElement(component, cmsFieldProps)}

            {underlyingValueHasChanged && !isSubmitting &&
            <FormHelperText>
                This value has been updated elsewhere
            </FormHelperText>}

        </>);

}
Example #14
Source File: DaoSettings.tsx    From homebase-app with MIT License 4 votes vote down vote up
DaoSettingsForm = withRouter(
  ({ submitForm, values, setFieldValue, errors, touched }: any) => {
    const theme = useTheme();
    const isMobile = useMediaQuery(theme.breakpoints.down("md"));

    const {
      data: tokenMetadata,
      isLoading: loading,
      error,
    } = useTokenMetadata(
      values?.governanceToken?.address,
      values?.governanceToken?.tokenId
    );

    useEffect(() => {
      if (tokenMetadata) {
        setFieldValue("governanceToken.tokenMetadata", tokenMetadata);
      }

      if (error) {
        setFieldValue("governanceToken.tokenMetadata", undefined);
      }
    }, [error, setFieldValue, tokenMetadata]);

    const { dispatch } = useContext(CreatorContext);
    const match = useRouteMatch();
    const history = useHistory();

    useEffect(() => {
      if (values) {
        dispatch({
          type: ActionTypes.UPDATE_NAVIGATION_BAR,
          next: {
            handler: () => {
              submitForm(values);
            },
            text: "CONTINUE",
          },
          back: {
            handler: () => history.push(`templates`),
            text: "BACK",
          },
        });
      }
    }, [dispatch, errors, history, match.path, match.url, submitForm, values]);

    return (
      <>
        <SecondContainer container item direction="row" spacing={2} wrap="wrap">
          <Grid item xs={isMobile ? 12 : 9}>
            <Typography variant="subtitle1" color="textSecondary">
              {" "}
              Token Address{" "}
            </Typography>
            <CustomInputContainer>
              <Field
                id="outlined-basic"
                placeholder="KT1...."
                name="governanceToken.address"
                component={CustomFormikTextField}
              />
            </CustomInputContainer>
            {errors.governanceToken?.address &&
            touched.governanceToken?.address ? (
              <ErrorText>{errors.governanceToken?.address}</ErrorText>
            ) : null}
          </Grid>
          <Grid item xs={isMobile ? 12 : 3}>
            <Typography variant="subtitle1" color="textSecondary">
              {" "}
              Token ID{" "}
            </Typography>
            <CustomInputContainer>
              <Field
                id="outlined-basic"
                placeholder="0"
                name="governanceToken.tokenId"
                component={CustomFormikTextField}
              />
            </CustomInputContainer>
            {errors.governanceToken?.tokenId &&
            touched.governanceToken?.tokenId ? (
              <ErrorText>{errors.governanceToken?.tokenId}</ErrorText>
            ) : null}
          </Grid>
          {tokenMetadata && !loading && (
            <MetadataContainer item xs={12}>
              <Typography variant="subtitle2" color="secondary">
                {tokenMetadata.name} ({tokenMetadata.symbol})
              </Typography>
            </MetadataContainer>
          )}

          <Grid item xs={isMobile ? 12 : 9}>
            <Typography variant="subtitle1" color="textSecondary">
              {" "}
              DAO Name{" "}
            </Typography>
            <CustomInputContainer>
              <Field
                name="name"
                inputProps={{ maxLength: 18 }}
                type="text"
                placeholder="My Group’s Token"
                component={CustomFormikTextField}
                InputProps={{
                  endAdornment: (
                    <InputAdornment position="start">
                      <Tooltip placement="bottom" title="DAO Name info">
                        <InfoIconInput color="secondary" />
                      </Tooltip>
                    </InputAdornment>
                  ),
                }}
              ></Field>
            </CustomInputContainer>
            {errors.name && touched.name ? (
              <ErrorText>{errors.name}</ErrorText>
            ) : null}
          </Grid>

          <Grid item xs={isMobile ? 12 : 3}>
            <Typography variant="subtitle1" color="textSecondary">
              {" "}
              Token Symbol{" "}
            </Typography>
            <CustomInputContainer>
              <Field
                name="symbol"
                type="text"
                inputProps={{
                  style: { textTransform: "uppercase" },
                  maxLength: 6,
                }}
                placeholder="MYTOK"
                component={CustomFormikTextField}
                InputProps={{
                  endAdornment: (
                    <InputAdornment position="start">
                      <Tooltip placement="bottom" title="Token symbol info">
                        <InfoIconInput color="secondary" />
                      </Tooltip>
                    </InputAdornment>
                  ),
                }}
              ></Field>
            </CustomInputContainer>
            {errors.symbol && touched.symbol ? (
              <ErrorText>{errors.symbol}</ErrorText>
            ) : null}
          </Grid>
        </SecondContainer>
        <SecondContainer container direction="row" alignItems="center">
          <Grid item xs={12}>
            <Typography variant="subtitle1" color="textSecondary">
              Description
            </Typography>
          </Grid>
          <TextareaContainer item xs={12}>
            <Field name="description">
              {() => (
                <CustomTextarea
                  maxLength={1500}
                  aria-label="empty textarea"
                  placeholder="This is what we’re about..."
                  value={getIn(values, "description")}
                  onChange={(newValue: any) => {
                    setFieldValue("description", newValue.target.value);
                  }}
                />
              )}
            </Field>
            <Tooltip placement="bottom" title="Description info">
              <InfoIcon color="secondary" />
            </Tooltip>
          </TextareaContainer>
          {errors.description && touched.description ? (
            <ErrorText>{errors.description}</ErrorText>
          ) : null}
        </SecondContainer>
        <SecondContainer item container direction="row" alignItems="center">
          <Grid item xs={12}>
            <Typography variant="subtitle1" color="textSecondary">
              {" "}
              Administrator{" "}
            </Typography>
          </Grid>
          <Grid item xs={12}>
            <CustomInputContainer>
              <Field
                name="administrator"
                type="text"
                placeholder="tz1PXn...."
                component={CustomFormikTextField}
                InputProps={{
                  endAdornment: (
                    <InputAdornment position="start">
                      <Tooltip placement="bottom" title="DAO Name info">
                        <InfoIconInput color="secondary" />
                      </Tooltip>
                    </InputAdornment>
                  ),
                }}
              ></Field>
            </CustomInputContainer>
            {errors.administrator && touched.administrator ? (
              <ErrorText>{errors.administrator}</ErrorText>
            ) : null}
          </Grid>
        </SecondContainer>
        <SecondContainer item container direction="row" alignItems="center">
          <Grid item xs={12}>
            <Typography variant="subtitle1" color="textSecondary">
              {" "}
              Guardian{" "}
            </Typography>
          </Grid>
          <Grid item xs={12}>
            <CustomInputContainer>
              <Field
                name="guardian"
                type="text"
                placeholder="tz1PXn...."
                component={CustomFormikTextField}
                InputProps={{
                  endAdornment: (
                    <InputAdornment position="start">
                      <Tooltip placement="bottom" title="DAO Name info">
                        <InfoIconInput color="secondary" />
                      </Tooltip>
                    </InputAdornment>
                  ),
                }}
              ></Field>
            </CustomInputContainer>
            {errors.guardian && touched.guardian ? (
              <ErrorText>{errors.guardian}</ErrorText>
            ) : null}
          </Grid>
        </SecondContainer>
      </>
    );
  }
)
Example #15
Source File: useForm.tsx    From SQForm with MIT License 4 votes vote down vote up
export function useForm<TValue, TChangeEvent>({
  name,
  onBlur,
  onChange,
}: UseFormParam<TChangeEvent>): UseFormReturn<TValue, TChangeEvent> {
  _handleError(name);

  const [field, meta, helpers] = useField<TValue>(name);
  const errorMessage = _transformErrorMessageToString<TValue>(meta);
  const isTouched = getIn(meta, 'touched');
  const isDirty = !isEqual(meta.initialValue, meta.value);
  const hasValue = _getHasValue(meta);
  const isError = !!errorMessage;
  const isRequired = errorMessage?.toLowerCase() === 'required';

  const getFieldStatus = () => {
    if (isRequired && !hasValue && !isDirty && !isTouched) {
      return 'REQUIRED';
    }
    if (isError) {
      return 'ERROR';
    }
    if (hasValue && !isError && isDirty) {
      return 'USER_FULFILLED';
    }

    return 'FULFILLED';
  };

  const isFieldRequired = getFieldStatus() === 'REQUIRED';
  const isFieldError = getFieldStatus() === 'ERROR';
  const isFulfilled = getFieldStatus() === 'USER_FULFILLED';

  const handleChange: ChangeHandler<TChangeEvent> = React.useCallback(
    (event) => {
      field.onChange(event);
      onChange && onChange(event);
    },
    [field, onChange]
  );

  const handleBlur = React.useCallback(
    (event) => {
      field.onBlur(event);
      onBlur && onBlur(event);
    },
    [field, onBlur]
  );

  const HelperTextComponent = React.useMemo(() => {
    if (isFieldError) {
      return (
        <>
          <WarningIcon color="error" style={SPACE_STYLE} />
          {errorMessage}
        </>
      );
    }
    if (isFieldRequired) {
      return (
        <>
          <WarningIcon color="disabled" style={SPACE_STYLE} />
          Required
        </>
      );
    }
    if (isFulfilled) {
      return (
        <VerifiedIcon
          style={{color: 'var(--color-palmLeaf)', ...SPACE_STYLE}}
        />
      );
    }
    return ' '; // return something so UI space always exists
  }, [isFieldError, isFieldRequired, isFulfilled, errorMessage]);

  return {
    formikField: {field, meta, helpers},
    fieldState: {
      errorMessage,
      isTouched,
      isError,
      isFieldError,
      isFieldRequired,
      isFulfilled,
    },
    fieldHelpers: {handleBlur, handleChange, HelperTextComponent},
  };
}
Example #16
Source File: SQFormAutocomplete.tsx    From SQForm with MIT License 4 votes vote down vote up
function SQFormAutocomplete({
  children,
  isDisabled = false,
  displayEmpty = false,
  label,
  name,
  onBlur,
  onChange,
  onInputChange,
  size = 'auto',
  lockWidthToField = true,
}: SQFormAutocompleteProps): React.ReactElement {
  const classes = useStyles();
  const autocompleteClasses = useAutocompleteStyles({lockWidthToField});

  const gridContainerRef = React.useRef<HTMLDivElement>(null);
  const baseWidth = calculateBaseWidth(gridContainerRef.current);
  const left = gridContainerRef.current?.getBoundingClientRect().left;
  const {setFieldValue, setTouched, values, touched} = useFormikContext();
  const [{value}] = useField(name);
  const {
    fieldState: {isFieldError, isFieldRequired},
    fieldHelpers: {HelperTextComponent},
  } = useForm({name});

  const initialValue = getInitialValue(children, value, displayEmpty);

  const [inputValue, setInputValue] = React.useState('');
  const prevValue = usePrevious(value);

  React.useEffect(() => {
    setInputValue(initialValue?.label || '');
  }, [initialValue]);

  React.useEffect(() => {
    // Form Reset
    if (prevValue && inputValue && !value) {
      setInputValue(displayEmpty ? EMPTY_OPTION.label : '');
    }
  }, [value, inputValue, name, prevValue, values, displayEmpty]);

  const handleAutocompleteBlur = React.useCallback(
    (event) => {
      setTouched({...touched, ...{[name]: true}});
      onBlur && onBlur(event);
    },
    [name, onBlur, setTouched, touched]
  );

  const handleAutocompleteChange = React.useCallback(
    (event, value, reason) => {
      const selectedValue = getIn(value, 'value');
      onChange && onChange(event, selectedValue, reason);
      if (reason === 'clear') {
        return setFieldValue(name, '');
      }
      return setFieldValue(name, selectedValue);
    },
    [name, onChange, setFieldValue]
  );

  const handleInputChange = React.useCallback(
    (event, value) => {
      setInputValue(value);
      onInputChange && onInputChange(event, value);
    },
    [onInputChange]
  );

  const options = children ? [...children] : [];
  displayEmpty && options.unshift(EMPTY_OPTION);

  return (
    <Grid item xs={size} ref={gridContainerRef} className={classes.grid}>
      <Autocomplete
        id={name}
        style={{width: '100%'}}
        disableListWrap
        disablePortal={!lockWidthToField}
        classes={autocompleteClasses}
        ListboxComponent={
          ListboxVirtualizedComponent as React.ComponentType<
            React.HTMLAttributes<HTMLElement>
          >
        }
        // Note: basewidth is not camel cased because React doesn't like it here
        ListboxProps={{basewidth: baseWidth, left, lockWidthToField}}
        options={options}
        onBlur={handleAutocompleteBlur}
        onChange={handleAutocompleteChange}
        onInputChange={handleInputChange}
        inputValue={inputValue}
        value={initialValue || null}
        disableClearable={isDisabled}
        disabled={isDisabled}
        getOptionLabel={(option) => option.label || ''}
        getOptionDisabled={(option: SQFormOption) =>
          option?.isDisabled || false
        }
        renderInput={(params) => {
          return (
            <TextField
              {...params}
              color="primary"
              disabled={isDisabled}
              error={isFieldError}
              fullWidth={true}
              InputLabelProps={{
                ...params.InputLabelProps,
                shrink: true,
              }}
              inputProps={{
                ...params.inputProps,
                disabled: isDisabled,
              }}
              FormHelperTextProps={{error: isFieldError}}
              name={name}
              label={label}
              helperText={!isDisabled && HelperTextComponent}
              required={isFieldRequired}
            />
          );
        }}
        renderOption={(option) => (
          <Typography variant="body2" noWrap>
            {option.label}
          </Typography>
        )}
      />
    </Grid>
  );
}
Example #17
Source File: SQFormAsyncAutocomplete.tsx    From SQForm with MIT License 4 votes vote down vote up
function SQFormAsyncAutocomplete({
  children,
  isDisabled = false,
  label,
  name,
  onBlur,
  onChange,
  onInputChange,
  handleAsyncInputChange,
  loading = false,
  open,
  onOpen,
  onClose,
  size = 'auto',
}: SQFormAsyncAutocompleteProps): React.ReactElement {
  const classes = useStyles();
  const {setFieldValue, setTouched, values} = useFormikContext();
  const [{value}] = useField(name);
  const {
    fieldState: {isFieldError, isFieldRequired},
    fieldHelpers: {HelperTextComponent},
  } = useForm({name});

  const initialValue = React.useMemo(() => {
    const optionInitialValue = children.find((option) => {
      if (option.value === value) {
        return option;
      }

      return null;
    });

    return optionInitialValue;
  }, [children, value]);

  const [inputValue, setInputValue] = React.useState('');
  const prevValue = usePrevious(value);

  React.useEffect(() => {
    setInputValue(initialValue?.label || '');
  }, [initialValue]);

  React.useEffect(() => {
    // Form Reset
    if (prevValue && inputValue && !value) {
      setInputValue('');
    }
  }, [value, inputValue, name, prevValue, values]);

  const handleAutocompleteBlur = React.useCallback(
    (event) => {
      setTouched({[name]: true});
      onBlur && onBlur(event);
    },
    [name, onBlur, setTouched]
  );

  const handleAutocompleteChange = React.useCallback(
    (event, value, reason) => {
      const selectedValue = getIn(value, 'value');
      onChange && onChange(event, selectedValue, reason);
      if (reason === 'clear') {
        return setFieldValue(name, '');
      }
      return setFieldValue(name, selectedValue);
    },
    [name, onChange, setFieldValue]
  );

  const handleInputChange = React.useCallback(
    (event, value) => {
      setInputValue(value);
      handleAsyncInputChange(value);
      onInputChange && onInputChange(event, value);
    },
    [handleAsyncInputChange, onInputChange]
  );

  return (
    <Grid item sm={size}>
      <Autocomplete
        id={name}
        style={{width: '100%'}}
        disableListWrap
        classes={classes}
        ListboxComponent={
          ListboxVirtualizedComponent as React.ComponentType<
            React.HTMLAttributes<HTMLElement>
          >
        }
        options={children}
        onBlur={handleAutocompleteBlur}
        onChange={handleAutocompleteChange}
        onInputChange={handleInputChange}
        open={open}
        onOpen={onOpen}
        onClose={onClose}
        inputValue={inputValue}
        disabled={isDisabled}
        getOptionLabel={(option) => option.label}
        getOptionDisabled={(option: SQFormOption) => option.isDisabled || false}
        renderInput={(params) => {
          return (
            <TextField
              {...params}
              color="primary"
              disabled={isDisabled}
              error={isFieldError}
              fullWidth={true}
              InputLabelProps={{
                ...params.InputLabelProps,
                shrink: true,
              }}
              inputProps={{
                ...params.inputProps,
                disabled: isDisabled,
              }}
              InputProps={{
                ...params.InputProps,
                endAdornment: (
                  <>
                    {loading ? (
                      <CircularProgress color="inherit" size={20} />
                    ) : null}
                    {params.InputProps.endAdornment}
                  </>
                ),
              }}
              FormHelperTextProps={{error: isFieldError}}
              name={name}
              label={label}
              helperText={!isDisabled && HelperTextComponent}
              required={isFieldRequired}
            />
          );
        }}
        renderOption={(option) => (
          <Typography variant="body2" noWrap>
            {option.label}
          </Typography>
        )}
      />
    </Grid>
  );
}