utils#callbackify TypeScript Examples

The following examples show how to use utils#callbackify. 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: ApiTypeSelector.tsx    From one-platform with MIT License 6 votes vote down vote up
ApiTypeSelector = ({ value, onChange, errorMsg }: Props): JSX.Element => {
  return (
    <FormGroup fieldId="datasource-type" label="Datasource type" isRequired>
      <Split hasGutter>
        {apiOptions.map(({ title, desc, image, type }) => (
          <SplitItem isFilled key={type}>
            <CatalogBigButton
              title={title}
              desc={desc}
              image={`${config.baseURL}/images/${image}`}
              isSelected={value === type}
              onClick={callbackify(onChange, type)}
            />
          </SplitItem>
        ))}
      </Split>
      {errorMsg && (
        <FormAlert>
          <Alert variant="danger" title={errorMsg} aria-live="polite" isInline />
        </FormAlert>
      )}
    </FormGroup>
  );
}
Example #2
Source File: EnvironmentFormSection.tsx    From one-platform with MIT License 4 votes vote down vote up
EnvironmentFormSection = ({
  schemaPos,
  handleSchemaValidation,
}: Props): JSX.Element => {
  const [envNames, setEnvNames] = useState(['prod', 'stage', 'qa', 'dev']);
  const { control, watch, getValues, setError, clearErrors, setValue } = useFormContext<FormData>();
  const isGraphqlAPI = watch(`schemas.${schemaPos}.category`) === ApiCategory.GRAPHQL;
  const { fields, append, remove } = useFieldArray({
    control,
    name: `schemas.${schemaPos}.environments`,
  });

  const handleAddNewEnvironment = () => {
    append(
      {
        name: '',
        apiBasePath: '',
        headers: [{ key: '', value: '', id: undefined }],
        schemaEndpoint: '',
        isPublic: false,
      },
      { shouldFocus: false }
    );
  };

  const onEnvNameClear = (onChange: (...event: any[]) => void) => {
    onChange('');
  };

  const onEnvNameSelect = (
    onChange: (...event: any[]) => void,
    event: React.MouseEventHandler,
    selection: string,
    isPlaceholder: boolean
  ) => {
    if (isPlaceholder) onEnvNameClear(onChange);
    else {
      onChange(selection);
    }
  };

  const onEnvNameCreate = (newSelection: string) => {
    if (envNames.findIndex((envName) => envName === newSelection) === -1) {
      setEnvNames((envState) => [...envState, newSelection]);
    }
  };

  const onSetIntrospectionQuery = (envIndex: number) => {
    const selectedEnv = `schemas.${schemaPos}.environments.${envIndex}` as const;
    const value = getValues(selectedEnv);
    setValue(`schemas.${schemaPos}.environments.${envIndex}.schemaEndpoint`, value.apiBasePath);
  };

  const setSchemaEndpointIsInvalid = (envIndex: number) => {
    setError(`schemas.${schemaPos}.environments.${envIndex}.schemaEndpoint`, {
      type: 'custom',
      message: `Failed to get ${isGraphqlAPI ? 'introspection url' : 'api schema'}`,
    });
  };

  const handleSchemaVerification = async (envIndex: number, schemaURL: string) => {
    if (!schemaURL) return;
    const isURL = isValidURL(schemaURL);
    const selectedEnv = `schemas.${schemaPos}.environments.${envIndex}` as const;
    if (isURL) {
      const envData = getValues(selectedEnv) as ApiEnvironmentType;
      const { slug, schemaEndpoint } = envData;
      const category = isGraphqlAPI ? ApiCategory.GRAPHQL : ApiCategory.REST;
      const headers = (envData?.headers || []).filter(({ key, value }) => key && value) as Header[];

      const data = await handleSchemaValidation({
        headers,
        schemaEndpoint,
        envSlug: slug,
        category,
      });
      if (!data?.file) {
        setSchemaEndpointIsInvalid(envIndex);
      } else {
        clearErrors(`schemas.${schemaPos}.environments.${envIndex}.schemaEndpoint`);
      }
    } else {
      setSchemaEndpointIsInvalid(envIndex);
      window.OpNotification.danger({ subject: 'Invalid schema url provided' });
    }
  };

  return (
    <Stack hasGutter className="pf-u-mt-md">
      <StackItem>
        <p className="pf-u-font-weight-bold">Environments</p>
      </StackItem>
      <StackItem>
        <Stack hasGutter>
          {fields.map((field, index) => (
            <StackItem key={field.id}>
              <Card id={field.id}>
                <CardBody>
                  <Grid hasGutter>
                    <GridItem span={3}>
                      <Controller
                        name={`schemas.${schemaPos}.environments.${index}.name`}
                        control={control}
                        rules={{ required: true }}
                        defaultValue=""
                        render={({ field: controllerField, fieldState: { error } }) => (
                          <FormGroup
                            fieldId={`schemas.${schemaPos}.environments.${index}.name`}
                            label="Name"
                            isRequired
                            validated={error ? 'error' : 'success'}
                            helperTextInvalid={error?.message}
                          >
                            <Select
                              variant={SelectVariant.typeahead}
                              typeAheadAriaLabel="Select a state"
                              onSelect={callbackify(onEnvNameSelect, controllerField.onChange)}
                              onClear={callbackify(onEnvNameClear, controllerField.onChange)}
                              selections={controllerField.value}
                              aria-label="env link"
                              placeholder="Enter environment name"
                              isCreatable
                              onCreateOption={onEnvNameCreate}
                              placeholderText="Enter environment name"
                            >
                              {envNames.map((env, envIndex) => (
                                <SelectOption key={`${env}-${envIndex + 1}`} value={env} />
                              ))}
                            </Select>
                          </FormGroup>
                        )}
                      />
                    </GridItem>
                    <GridItem span={8}>
                      <Controller
                        name={`schemas.${schemaPos}.environments.${index}.apiBasePath`}
                        control={control}
                        rules={{ required: true }}
                        defaultValue=""
                        render={({ field: controllerField, fieldState: { error } }) => (
                          <FormGroup
                            fieldId={`schemas.${schemaPos}.environments.${index}.apiBasePath`}
                            label="API Base Path"
                            isRequired
                            validated={error ? 'error' : 'success'}
                            helperTextInvalid={error?.message}
                          >
                            <TextInput
                              aria-label="env link"
                              placeholder="Enter base path for the api"
                              {...controllerField}
                            />
                          </FormGroup>
                        )}
                      />
                    </GridItem>
                    <GridItem
                      span={1}
                      className="pf-u-display-flex pf-u-justify-content-center pf-u-align-items-flex-end"
                    >
                      <Button
                        variant="secondary"
                        aria-label="Remove"
                        onClick={callbackify(remove, index)}
                        className={styles['trash-button']}
                      >
                        <TrashIcon />
                      </Button>
                    </GridItem>
                    <GridItem span={12}>
                      <Controller
                        name={`schemas.${schemaPos}.environments.${index}.schemaEndpoint`}
                        control={control}
                        rules={{ required: true }}
                        defaultValue=""
                        render={({ field: { ...controllerField }, fieldState: { error } }) => (
                          <EnvSchemaField
                            isGraphqlAPI={isGraphqlAPI}
                            isError={Boolean(error)}
                            errorMessage={error?.message}
                            envIndex={index}
                            onCopyValue={() => onSetIntrospectionQuery(index)}
                            onRedoValidation={() =>
                              handleSchemaVerification(index, controllerField.value || '')
                            }
                            {...controllerField}
                          />
                        )}
                      />
                    </GridItem>
                    <GridItem span={12}>
                      <EnvHeaderFormSection schemaPos={schemaPos} envPos={index} />
                    </GridItem>
                    <GridItem span={12}>
                      <Controller
                        name={`schemas.${schemaPos}.environments.${index}.isPublic`}
                        defaultValue={false}
                        render={({ field: controllerField }) => (
                          <Checkbox
                            label="Is this API accessible from public?"
                            description="Tick this option if your environment can be accessed without VPN"
                            isChecked={controllerField.value}
                            id={`api-schema-${schemaPos}-env-${index}-internal`}
                            {...controllerField}
                          />
                        )}
                      />
                    </GridItem>
                  </Grid>
                </CardBody>
              </Card>
            </StackItem>
          ))}
        </Stack>
      </StackItem>
      <StackItem>
        <Button
          variant="link"
          icon={<PlusIcon size="sm" />}
          className="pf-u-p-0 pf-u-mb-lg"
          onClick={handleAddNewEnvironment}
        >
          Add Environment
        </Button>
      </StackItem>
    </Stack>
  );
}
Example #3
Source File: EnvHeaderFormSection.tsx    From one-platform with MIT License 4 votes vote down vote up
EnvHeaderFormSection = ({ schemaPos, envPos }: Props): JSX.Element => {
  const { control, watch } = useFormContext<FormData>();
  const { fields, append, remove } = useFieldArray({
    control,
    name: `schemas.${schemaPos}.environments.${envPos}.headers`,
  });
  const headerFields = watch(`schemas.${schemaPos}.environments.${envPos}.headers`);

  const handleRemoveHeader = (indexToRemove: number) => {
    remove(indexToRemove);
  };

  return (
    <Stack hasGutter>
      <StackItem style={{ marginBottom: 0 }}>
        <Split>
          <SplitItem isFilled>
            <p className="pf-u-font-weight-bold" style={{ marginBottom: '-1rem' }}>
              Headers
            </p>
          </SplitItem>
          <SplitItem>
            <Button
              icon={<PlusIcon />}
              variant="link"
              isSmall
              className="pf-u-mb-xs"
              onClick={() => append({ id: undefined, value: '', key: '' })}
            >
              Add Header
            </Button>
          </SplitItem>
        </Split>
      </StackItem>
      {fields.map((field, index) => (
        <StackItem key={field.id}>
          <Split hasGutter>
            <SplitItem isFilled>
              <Controller
                name={`schemas.${schemaPos}.environments.${envPos}.headers.${index}.key`}
                control={control}
                defaultValue=""
                render={({ field: controllerField, fieldState: { error } }) => (
                  <FormGroup
                    fieldId={`headers.${index}.key`}
                    isRequired
                    validated={error ? 'error' : 'success'}
                    helperTextInvalid={error?.message}
                  >
                    <TextInput
                      aria-label="header name"
                      placeholder="Content-Type"
                      {...controllerField}
                    />
                  </FormGroup>
                )}
              />
            </SplitItem>
            <SplitItem isFilled>
              <Controller
                name={`schemas.${schemaPos}.environments.${envPos}.headers.${index}.value`}
                control={control}
                defaultValue=""
                render={({ field: controllerField, fieldState: { error } }) => (
                  <FormGroup
                    fieldId={`headers.${index}.value`}
                    isRequired
                    validated={error ? 'error' : 'success'}
                    helperTextInvalid={error?.message}
                  >
                    <TextInput
                      aria-label="header url"
                      isDisabled={Boolean(headerFields?.[index].id)}
                      placeholder="**********"
                      type="password"
                      {...controllerField}
                    />
                  </FormGroup>
                )}
              />
            </SplitItem>
            <SplitItem>
              <Button
                variant="secondary"
                onClick={callbackify(handleRemoveHeader, index)}
                className={styles['trash-button']}
              >
                <TrashIcon />
              </Button>
            </SplitItem>
          </Split>
        </StackItem>
      ))}
    </Stack>
  );
}
Example #4
Source File: APIListPage.tsx    From one-platform with MIT License 4 votes vote down vote up
APIListPage = (): JSX.Element => {
  const navigate = useNavigate();

  // query param strings
  const query = useQueryParams();
  const mid = query.get('mid');
  const defaultSearch = query.get('search');

  // filters, search, sorting
  const [isSortSelectOpen, setSortSelect] = useToggle();
  const [sortOption, setSortOption] = useState(SortBy.RECENTLY_ADDED);
  const [filters, setFilters] = useState<{ type: null | ApiCategory; search: string }>({
    type: null,
    search: defaultSearch || '',
  });
  const { pagination, onPerPageSelect, onSetPage, onResetPagination } = usePagination({
    page: 1,
    perPage: 20,
  });
  const debouncedSearch = useDebounce(filters.search);

  // graphql query hooks
  const { isLoading: isApiListLoading, data: namespaceList } = useGetNamespaceList({
    limit: pagination.perPage,
    offset: (pagination.page - 1) * pagination.perPage,
    apiCategory: filters.type,
    search: debouncedSearch,
    sortBy: sortOption === SortBy.RECENTLY_ADDED ? 'CREATED_ON' : 'UPDATED_ON',
    mid,
  });
  const { isLoading: isNamespaceStatLoading, data: namespaceStats } = useGetNamespaceStats({
    search: debouncedSearch,
    mid,
  });

  const handleApiOwnersRender = useCallback((owners: ApiOwnerType[]) => {
    return owners.map((owner) =>
      owner.group === ApiEmailGroup.USER ? owner.user.cn : owner.email
    );
  }, []);

  const onStatCardClick = (cardType: 'total' | 'rest' | 'graphql') => {
    onResetPagination();
    if (cardType === 'total') {
      setFilters((state) => ({ ...state, type: null }));
    } else {
      setFilters((state) => ({ ...state, type: cardType.toUpperCase() as ApiCategory }));
    }
  };

  const onSearch = (search: string) => {
    setFilters((state) => ({ ...state, search }));
  };

  const onSortSelect = (
    event: React.MouseEvent | React.ChangeEvent,
    value: string | SelectOptionObject,
    isPlaceholder?: boolean
  ) => {
    if (isPlaceholder) setSortOption(SortBy.RECENTLY_ADDED);
    else setSortOption(value as SortBy);

    setSortSelect.off(); // close the select
  };

  const onCardClick = (id: string) => {
    navigate(id);
  };

  const namespaceCount = namespaceStats?.getApiCategoryCount;
  const namespaces = namespaceList?.listNamespaces?.data;

  const isNamespaceEmpty = !isApiListLoading && namespaces?.length === 0;

  return (
    <>
      <Header />
      <Divider />
      <PageSection variant="light" isWidthLimited className="pf-m-align-center">
        <Grid hasGutter>
          <Grid hasGutter span={12}>
            {stats.map(({ key, type, image }) => (
              <GridItem
                key={`api-select-${type}`}
                span={4}
                className={styles['api-list--stat-card']}
                type={key}
              >
                <StatCard
                  value={namespaceCount?.[key]}
                  category={type}
                  isLoading={isNamespaceStatLoading}
                  onClick={callbackify(onStatCardClick, key)}
                  isSelected={filters.type ? filters.type.toLowerCase() === key : key === 'total'}
                >
                  <img
                    src={`${config.baseURL}/images/${image}`}
                    alt={`api-select-${type}`}
                    style={{ height: '48px' }}
                  />
                </StatCard>
              </GridItem>
            ))}
          </Grid>
          <GridItem className="pf-u-my-md">
            <Split hasGutter className={styles['api-list--table-filter--container']}>
              <SplitItem isFilled>
                <Link to={ApiCatalogLinks.AddNewApiPage}>
                  <Button>Add API</Button>
                </Link>
              </SplitItem>
              <SplitItem className="pf-u-w-33">
                <Form>
                  <FormGroup fieldId="search">
                    <TextInput
                      aria-label="Search API"
                      placeholder="Search for APIs"
                      type="search"
                      iconVariant="search"
                      value={filters.search}
                      onChange={onSearch}
                    />
                  </FormGroup>
                </Form>
              </SplitItem>
              <SplitItem style={{ width: '180px' }}>
                <Select
                  isOpen={isSortSelectOpen}
                  onToggle={setSortSelect.toggle}
                  selections={sortOption}
                  onSelect={onSortSelect}
                >
                  {[
                    <SelectOption key="select-sort-placeholder" value="Sort by" isDisabled />,
                    <SelectOption
                      key={`select-sort:${SortBy.RECENTLY_ADDED}`}
                      value={SortBy.RECENTLY_ADDED}
                    />,
                    <SelectOption
                      key={`select-sort:${SortBy.RECENTLY_MODIFIED}`}
                      value={SortBy.RECENTLY_MODIFIED}
                    />,
                  ]}
                </Select>
              </SplitItem>
            </Split>
          </GridItem>

          {isApiListLoading ? (
            <Bullseye className="pf-u-mt-lg">
              <Spinner size="xl" />
            </Bullseye>
          ) : (
            namespaces?.map(({ id, name, updatedOn, owners, schemas, slug }) => (
              <GridItem
                span={12}
                key={id}
                className="catalog-nav-link"
                onClick={callbackify(onCardClick, slug)}
              >
                <ApiDetailsCard
                  title={name}
                  owners={handleApiOwnersRender(owners)}
                  updatedAt={updatedOn}
                  schemas={schemas.map(({ name: schemaName, category }) => ({
                    name: schemaName,
                    type: category,
                  }))}
                />
              </GridItem>
            ))
          )}
          {isNamespaceEmpty && (
            <EmptyState>
              <EmptyStateIcon icon={CubesIcon} />
              <Title headingLevel="h4" size="lg">
                No API found
              </Title>
              <EmptyStateBody>Add an API to fill this gap</EmptyStateBody>
            </EmptyState>
          )}
        </Grid>
      </PageSection>
      <PageSection variant="light" isWidthLimited className="pf-m-align-center pf-u-pb-2xl">
        <Pagination
          itemCount={namespaceList?.listNamespaces?.count || 0}
          widgetId="pagination-options-menu-bottom"
          perPage={pagination.perPage}
          page={pagination.page}
          onSetPage={(_, page) => onSetPage(page)}
          onPerPageSelect={(_, perPage) => onPerPageSelect(perPage)}
          isCompact
        />
      </PageSection>
    </>
  );
}