@patternfly/react-core#ToolbarItem JavaScript Examples

The following examples show how to use @patternfly/react-core#ToolbarItem. 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: FilterDropdown.js    From edge-frontend with Apache License 2.0 6 votes vote down vote up
FilterDropdown = ({ filters, dropdown, setDropdown }) => {
  const toggle = () => {
    setDropdown((prevState) => ({
      ...prevState,
      isOpen: !prevState.isOpen,
    }));
  };

  const select = (_e, selection) => {
    setDropdown({
      selected: selection,
      isOpen: false,
    });
  };

  return (
    <>
      {filters.length > 1 ? (
        <ToolbarItem data-testid="filter-dropdown-testid" className="pf-u-mr-0">
          <Select
            variant="single"
            aria-label="Select input for filters"
            width="11rem"
            onToggle={toggle}
            onSelect={select}
            selections={dropdown.selected}
            isOpen={dropdown.isOpen}
            toggleIcon={<FilterIcon />}
          >
            {filters.map((filter, index) => (
              <SelectOption key={index} value={filter.label} />
            ))}
          </Select>
        </ToolbarItem>
      ) : null}
    </>
  );
}
Example #2
Source File: ToolbarFooter.js    From edge-frontend with Apache License 2.0 6 votes vote down vote up
ToolbarFooter = ({
  isLoading,
  count,
  perPage,
  setPerPage,
  page,
  setPage,
}) => {
  return (
    <Toolbar style={{ padding: '16px' }} id="toolbar-footer">
      <ToolbarContent>
        <ToolbarItem variant="pagination" align={{ default: 'alignRight' }}>
          {isLoading ? (
            <Skeleton width="400px" />
          ) : count > 0 ? (
            <Pagination
              data-testid="pagination-footer-test-id"
              itemCount={count}
              perPage={perPage}
              page={page}
              onSetPage={(_e, pageNumber) => setPage(pageNumber)}
              widgetId="pagination-options-menu-top"
              onPerPageSelect={(_e, perPage) => setPerPage(perPage)}
            />
          ) : null}
        </ToolbarItem>
      </ToolbarContent>
    </Toolbar>
  );
}
Example #3
Source File: ToolbarHeader.js    From edge-frontend with Apache License 2.0 6 votes vote down vote up
ToolbarButtons = ({ buttons }) => {
  return buttons.map(({ title, click }, index) => (
    <ToolbarItem key={index}>
      <Button onClick={click} variant="primary">
        {title}
      </Button>
    </ToolbarItem>
  ));
}
Example #4
Source File: ToolbarKebab.js    From edge-frontend with Apache License 2.0 6 votes vote down vote up
ToolbarKebab = ({ kebabItems }) => {
  const [kebabIsOpen, setKebabIsOpen] = useState(false);

  const dropdownItems = kebabItems.map(
    ({ title, isDisabled, onClick }, index) => (
      <DropdownItem
        key={index}
        onClick={onClick ? onClick : () => {}}
        isDisabled={isDisabled}
      >
        {title}
      </DropdownItem>
    )
  );

  return (
    <ToolbarItem>
      <Dropdown
        toggle={
          <KebabToggle onToggle={() => setKebabIsOpen((prev) => !prev)} />
        }
        isOpen={kebabIsOpen}
        isPlain
        dropdownItems={dropdownItems}
      />
    </ToolbarItem>
  );
}
Example #5
Source File: BulkSelect.js    From edge-frontend with Apache License 2.0 5 votes vote down vote up
BulkSelect = ({
  checkedRows,
  handleBulkSelect,
  handlePageSelect,
  handleNoneSelect,
  displayedRowsLength,
}) => {
  const isAllSelected = checkedRows.length === displayedRowsLength;
  const isPartiallySelected = checkedRows.length > 0 ? null : false;
  const [selectAllToggle, setSelectAllToggle] = useState(false);

  return (
    <>
      <ToolbarItem variant="bulk-select">
        <Dropdown
          toggle={
            <DropdownToggle
              id="stacked-example-toggle"
              splitButtonItems={[
                <DropdownToggleCheckbox
                  id="example-checkbox-2"
                  key="split-checkbox"
                  aria-label="Select all"
                  isChecked={isAllSelected ? true : isPartiallySelected}
                  onChange={isAllSelected ? handleNoneSelect : handlePageSelect}
                >
                  {checkedRows.length > 0 && `${checkedRows.length} selected`}
                </DropdownToggleCheckbox>,
              ]}
              onToggle={() => setSelectAllToggle((prevState) => !prevState)}
            />
          }
          isOpen={selectAllToggle}
          dropdownItems={[
            <DropdownItem key="all" onClick={handleBulkSelect}>
              Select all
            </DropdownItem>,
            <DropdownItem
              key="page"
              onClick={handlePageSelect}
              isDisabled={isAllSelected}
            >
              Select page
            </DropdownItem>,
            <DropdownItem
              key="none"
              onClick={handleNoneSelect}
              isDisabled={checkedRows.length === 0}
            >
              Select none
            </DropdownItem>,
          ]}
        />
      </ToolbarItem>
    </>
  );
}
Example #6
Source File: ToolbarHeader.js    From edge-frontend with Apache License 2.0 5 votes vote down vote up
ToolbarHeader = ({
  toolbarButtons,
  filters,
  setFilterValues,
  filterValues,
  chipsArray,
  setChipsArray,
  isLoading,
  count,
  perPage,
  setPerPage,
  page,
  setPage,
  toggleButton,
  toggleAction,
  toggleState,
  children,
  kebabItems,
}) => {
  return (
    <Toolbar
      style={{ padding: '16px' }}
      id="toolbar-header"
      data-testid="toolbar-header-testid"
    >
      <ToolbarContent>
        <FilterControls
          filters={filters}
          filterValues={filterValues}
          setFilterValues={setFilterValues}
        >
          {children}
        </FilterControls>
        {toolbarButtons && <ToolbarButtons buttons={toolbarButtons} />}
        {toggleButton && (
          <ToggleGroup>
            {toggleButton.map((btn) => (
              <ToggleGroupItem
                key={btn.key}
                text={btn.title}
                isSelected={toggleState === btn.key}
                onChange={() => toggleAction(btn.key)}
              />
            ))}
          </ToggleGroup>
        )}
        {kebabItems && <ToolbarKebab kebabItems={kebabItems} />}
        <ToolbarItem variant="pagination" align={{ default: 'alignRight' }}>
          {isLoading ? (
            <Skeleton width="200px" />
          ) : count > 0 ? (
            <Pagination
              data-testid="pagination-header-test-id"
              itemCount={count}
              perPage={perPage}
              page={page}
              onSetPage={(_e, pageNumber) => setPage(pageNumber)}
              widgetId="pagination-options-menu-top"
              onPerPageSelect={(_e, perPage) => setPerPage(perPage)}
              isCompact
            />
          ) : null}
        </ToolbarItem>
      </ToolbarContent>
      <ToolbarContent>
        <ToolbarItem variant="chip-group" spacer={{ default: 'spacerNone' }}>
          <FilterChip
            filterValues={filterValues}
            setFilterValues={setFilterValues}
            chipsArray={chipsArray}
            setChipsArray={setChipsArray}
            setPage={setPage}
          />
        </ToolbarItem>
      </ToolbarContent>
    </Toolbar>
  );
}
Example #7
Source File: AccessRequestsTable.js    From access-requests-frontend with Apache License 2.0 4 votes vote down vote up
AccessRequestsTable = ({ isInternal }) => {
  const columns = isInternal
    ? [
        'Request ID',
        'Account number',
        'Start date',
        'End date',
        'Created',
        'Status',
      ]
    : [
        'Request ID',
        'First name',
        'Last name',
        'Start date',
        'End date',
        'Created',
        'Decision',
      ];

  // Sorting
  const [activeSortIndex, setActiveSortIndex] = React.useState(
    isInternal ? 4 : 5
  );
  const [activeSortDirection, setActiveSortDirection] = React.useState('desc');
  const onSort = (_ev, index, direction) => {
    setActiveSortIndex(index);
    setActiveSortDirection(direction);
  };

  // Pagination
  const [page, setPage] = React.useState(1);
  const [perPage, setPerPage] = React.useState(20);
  const AccessRequestsPagination = ({ id }) => (
    <Pagination
      itemCount={numRows}
      perPage={perPage}
      page={page}
      onSetPage={(_ev, pageNumber) => setPage(pageNumber)}
      id={'access-requests-table-pagination-' + id}
      variant={id}
      perPageOptions={[5, 10, 20, 50].map((n) => ({ title: n, value: n }))}
      onPerPageSelect={(_ev, perPage) => {
        setPage(1);
        setPerPage(perPage);
      }}
      isCompact={id === 'top'}
    />
  );

  AccessRequestsPagination.propTypes = {
    id: PropTypes.string,
  };

  // Filtering
  const [isDropdownOpen, setIsDropdownOpen] = React.useState(false);
  const [filterColumn, setFilterColumn] = React.useState(
    columns[isInternal ? 1 : 6]
  );
  const [isSelectOpen, setIsSelectOpen] = React.useState(false);
  const [statusSelections, setStatusSelections] = React.useState([]);

  // Harder than it needs to be to match rest of RBAC which doesn't wait
  // for user to click a button or press enter.
  const [accountFilter, setAccountFilter] = React.useState('');
  const [filtersDirty, setFiltersDirty] = React.useState(false);
  const hasFilters = statusSelections.length > 0 || accountFilter;

  // Row loading
  const [isLoading, setIsLoading] = React.useState(true);
  const [numRows, setNumRows] = React.useState(0);
  const [rows, setRows] = React.useState([]);
  const dispatch = useDispatch();
  const fetchAccessRequests = () => {
    setIsLoading(true);
    const listUrl = new URL(
      `${window.location.origin}${API_BASE}/cross-account-requests/`
    );

    isInternal
      ? listUrl.searchParams.append('query_by', 'user_id')
      : listUrl.searchParams.append('query_by', 'target_account');

    listUrl.searchParams.append('offset', (page - 1) * perPage);
    listUrl.searchParams.append('limit', perPage);
    // https://github.com/RedHatInsights/insights-rbac/blob/master/rbac/api/cross_access/view.py
    if (accountFilter) {
      listUrl.searchParams.append('account', accountFilter);
    }
    if (statusSelections.length > 0) {
      listUrl.searchParams.append('status', statusSelections.join(','));
    }
    const orderBy = `${activeSortDirection === 'desc' ? '-' : ''}${columns[
      activeSortIndex
    ]
      .toLowerCase()
      .replace(' ', '_')}`;
    listUrl.searchParams.append('order_by', orderBy);

    apiInstance
      .get(listUrl.href, { headers: { Accept: 'application/json' } })
      .then((res) => {
        setNumRows(res.meta.count);
        setRows(
          res.data.map((d) =>
            isInternal
              ? [
                  d.request_id,
                  d.target_account,
                  d.start_date,
                  d.end_date,
                  d.created,
                  d.status,
                ]
              : [
                  d.request_id,
                  d.first_name,
                  d.last_name,
                  d.start_date,
                  d.end_date,
                  d.created,
                  d.status,
                ]
          )
        );
        setIsLoading(false);
      })
      .catch((err) => {
        setIsLoading(false);
        dispatch(
          addNotification({
            variant: 'danger',
            title: 'Could not list access requests',
            description: err.message,
          })
        );
      });
  };
  const debouncedAccountFilter = useDebounce(accountFilter, 400);
  React.useEffect(() => {
    fetchAccessRequests();
  }, [
    debouncedAccountFilter,
    statusSelections,
    activeSortIndex,
    activeSortDirection,
    perPage,
    page,
  ]);

  // Modal actions
  const [openModal, setOpenModal] = React.useState({ type: null });
  const onModalClose = (isChanged) => {
    setOpenModal({ type: null });
    if (isChanged) {
      fetchAccessRequests();
    }
  };
  const modals = (
    <React.Fragment>
      {openModal.type === 'cancel' && (
        <CancelRequestModal
          requestId={openModal.requestId}
          onClose={onModalClose}
        />
      )}
      {['edit', 'create'].includes(openModal.type) && (
        <EditRequestModal
          variant={openModal.type}
          requestId={openModal.requestId}
          onClose={onModalClose}
        />
      )}
    </React.Fragment>
  );

  // Rendering
  const createButton = isInternal && (
    <Button variant="primary" onClick={() => setOpenModal({ type: 'create' })}>
      Create request
    </Button>
  );
  if (rows.length === 0 && !isLoading && !filtersDirty) {
    return (
      <Bullseye style={{ height: 'auto' }} className="pf-u-mt-lg">
        <EmptyState variant="large">
          <EmptyStateIcon icon={PlusCircleIcon} />
          <Title headingLevel="h3" size="lg">
            {isInternal ? 'No access requests' : 'You have no access requests'}
          </Title>
          <EmptyStateBody>
            {isInternal
              ? 'Click the button below to create an access request.'
              : 'You have no pending Red Hat access requests.'}
          </EmptyStateBody>
          {createButton}
        </EmptyState>
        {modals}
      </Bullseye>
    );
  }

  const selectLabelId = 'filter-status';
  const selectPlaceholder = `Filter by ${uncapitalize(
    columns[columns.length - 1]
  )}`;
  const clearFiltersButton = (
    <Button
      variant="link"
      onClick={() => {
        setStatusSelections([]);
        setAccountFilter('');
        setPage(1);
      }}
    >
      Clear filters
    </Button>
  );
  const toolbar = (
    <Toolbar id="access-requests-table-toolbar">
      <ToolbarContent>
        <ToolbarItem>
          <InputGroup>
            <Dropdown
              isOpen={isDropdownOpen}
              onSelect={(ev) => {
                setIsDropdownOpen(false);
                setFilterColumn(ev.target.value);
                setIsSelectOpen(false);
                setFiltersDirty(true);
              }}
              toggle={
                <DropdownToggle
                  onToggle={(isOpen) => setIsDropdownOpen(isOpen)}
                >
                  <FilterIcon /> {filterColumn}
                </DropdownToggle>
              }
              // https://marvelapp.com/prototype/257je526/screen/74764732
              dropdownItems={(isInternal ? [1, 5] : [6])
                .map((i) => columns[i])
                .map((colName) => (
                  // Filterable columns are RequestID, AccountID, and Status
                  <DropdownItem
                    key={colName}
                    value={colName}
                    component="button"
                  >
                    {capitalize(colName)}
                  </DropdownItem>
                ))}
            />
            {['Status', 'Decision'].includes(filterColumn) && (
              <React.Fragment>
                <span id={selectLabelId} hidden>
                  {selectPlaceholder}
                </span>
                <Select
                  aria-labelledby={selectLabelId}
                  variant="checkbox"
                  aria-label="Select statuses"
                  onToggle={(isOpen) => setIsSelectOpen(isOpen)}
                  onSelect={(_ev, selection) => {
                    setFiltersDirty(true);
                    if (statusSelections.includes(selection)) {
                      setStatusSelections(
                        statusSelections.filter((s) => s !== selection)
                      );
                    } else {
                      setStatusSelections([...statusSelections, selection]);
                    }
                    setPage(1);
                  }}
                  isOpen={isSelectOpen}
                  selections={Array.from(statusSelections)}
                  isCheckboxSelectionBadgeHidden
                  placeholderText={selectPlaceholder}
                >
                  {statuses.map((status) => (
                    <SelectOption key={status} value={status}>
                      {capitalize(status)}
                    </SelectOption>
                  ))}
                </Select>
              </React.Fragment>
            )}
            {filterColumn === 'Account number' && (
              <form
                style={{ display: 'flex' }}
                onSubmit={(ev) => ev.preventDefault()}
              >
                <TextInput
                  name={`${filterColumn}-filter`}
                  id={`${filterColumn}-filter`}
                  type="search"
                  iconVariant="search"
                  placeholder={`Filter by ${uncapitalize(filterColumn)}`}
                  aria-label={`${filterColumn} search input`}
                  value={accountFilter}
                  onChange={(val) => {
                    setAccountFilter(val), setFiltersDirty(true), setPage(1);
                  }}
                />
              </form>
            )}
          </InputGroup>
        </ToolbarItem>
        <ToolbarItem>{createButton}</ToolbarItem>
        <ToolbarItem variant="pagination" align={{ default: 'alignRight' }}>
          <AccessRequestsPagination id="top" />
        </ToolbarItem>
      </ToolbarContent>
      <ToolbarContent>
        <ChipGroup categoryName="Status">
          {statusSelections.map((status) => (
            <Chip
              key={status}
              onClick={() => {
                setStatusSelections(
                  statusSelections.filter((s) => s !== status)
                );
                setPage(1);
              }}
            >
              {status}
            </Chip>
          ))}
        </ChipGroup>
        {accountFilter && (
          <ChipGroup categoryName="Account number">
            <Chip
              onClick={() => {
                setAccountFilter(''), setPage(1);
              }}
            >
              {accountFilter}
            </Chip>
          </ChipGroup>
        )}
        {hasFilters && clearFiltersButton}
      </ToolbarContent>
    </Toolbar>
  );
  function getColumnWidth(columnIndex) {
    if (isInternal) {
      return columnIndex === 0 ? 30 : 15;
    }

    return [0, 6].includes(columnIndex) ? 20 : 10;
  }
  const { url } = useRouteMatch();
  const table = (
    <TableComposable aria-label="Access requests table" variant="compact">
      <Thead>
        <Tr>
          {columns.map((column, columnIndex) => (
            <Th
              key={columnIndex}
              {...(!column.includes('name') &&
                column !== 'Decision' && {
                  sort: {
                    sortBy: {
                      index: activeSortIndex,
                      direction: activeSortDirection,
                    },
                    onSort,
                    columnIndex,
                  },
                })}
              width={getColumnWidth(columnIndex)}
            >
              {column}
            </Th>
          ))}
          {isInternal && <Th />}
        </Tr>
      </Thead>
      <Tbody>
        {isLoading
          ? [...Array(rows.length || perPage).keys()].map((i) => (
              <Tr key={i}>
                {columns.map((name, j) => (
                  <Td key={j} dataLabel={name}>
                    <div
                      style={{ height: '30px' }}
                      className="ins-c-skeleton ins-c-skeleton__md"
                    >
                      {' '}
                    </div>
                  </Td>
                ))}
              </Tr>
            ))
          : rows.map((row, rowIndex) => (
              <Tr key={rowIndex}>
                <Td dataLabel={columns[0]}>
                  <Link to={`${url}${url.endsWith('/') ? '' : '/'}${row[0]}`}>
                    {row[0]}
                  </Link>
                </Td>
                <Td dataLabel={columns[1]}>{row[1]}</Td>
                <Td dataLabel={columns[2]}>{row[2]}</Td>
                <Td dataLabel={columns[3]}>{row[3]}</Td>
                <Td dataLabel={columns[4]}>{row[4]}</Td>
                {isInternal ? (
                  <Td dataLabel={columns[5]}>
                    <StatusLabel
                      requestId={row[0]}
                      status={row[5]}
                      onLabelClick={() => {
                        setStatusSelections([
                          ...statusSelections.filter((s) => s !== status),
                          status,
                        ]);
                        setPage(1);
                      }}
                      hideActions
                    />
                  </Td>
                ) : (
                  <Td dataLabel={columns[5]}>{row[5]}</Td>
                )}
                {isInternal ? (
                  // Different actions based on status
                  <Td
                    actions={getInternalActions(row[5], row[0], setOpenModal)}
                  />
                ) : (
                  <Td dataLabel={columns[6]}>
                    <StatusLabel requestId={row[0]} status={row[6]} />
                  </Td>
                )}
              </Tr>
            ))}
        {rows.length === 0 && hasFilters && (
          <Tr>
            <Td colSpan={columns.length}>
              <EmptyState variant="small">
                <EmptyStateIcon icon={SearchIcon} />
                <Title headingLevel="h2" size="lg">
                  No matching requests found
                </Title>
                <EmptyStateBody>
                  No results match the filter criteria. Remove all filters or
                  clear all filters to show results.
                </EmptyStateBody>
                {clearFiltersButton}
              </EmptyState>
            </Td>
          </Tr>
        )}
      </Tbody>
    </TableComposable>
  );

  return (
    <React.Fragment>
      {toolbar}
      {table}
      <AccessRequestsPagination id="bottom" />
      {modals}
    </React.Fragment>
  );
}
Example #8
Source File: MUARolesTable.js    From access-requests-frontend with Apache License 2.0 4 votes vote down vote up
MUARolesTable = ({
  roles: selectedRoles,
  setRoles: setSelectedRoles,
}) => {
  const isReadOnly = setSelectedRoles === undefined;
  const columns = ['Role name', 'Role description', 'Permissions'];
  const [rows, setRows] = React.useState(Array.from(rolesCache));
  const [applications, setApplications] = React.useState(applicationsCache);
  React.useEffect(() => {
    if (rolesCache.length === 0 || applicationsCache.length === 0) {
      apiInstance
        .get(
          `${API_BASE}/roles/?limit=9999&order_by=display_name&add_fields=groups_in_count`,
          { headers: { Accept: 'application/json' } }
        )
        .then(({ data }) => {
          data.forEach((role) => {
            role.isExpanded = false;
            role.permissions = role.accessCount;
          });
          rolesCache = data.map((role) => Object.assign({}, role));
          setRows(data);

          // Build application filter from data
          const apps = Array.from(
            data
              .map((role) => role.applications)
              .flat()
              .reduce((acc, cur) => {
                acc.add(cur);
                return acc;
              }, new Set())
          ).sort();
          applicationsCache = apps;
          setApplications(apps);
        })
        .catch((err) =>
          dispatch(
            addNotification({
              variant: 'danger',
              title: 'Could not fetch roles list',
              description: err.message,
            })
          )
        );
    }
  }, []);

  // Sorting
  const [activeSortIndex, setActiveSortIndex] = React.useState('name');
  const [activeSortDirection, setActiveSortDirection] = React.useState('asc');
  const onSort = (_ev, index, direction) => {
    setActiveSortIndex(index);
    setActiveSortDirection(direction);
  };

  // Filtering
  const [isDropdownOpen, setIsDropdownOpen] = React.useState(false);
  const [filterColumn, setFilterColumn] = React.useState(columns[0]);
  const [isSelectOpen, setIsSelectOpen] = React.useState(false);
  const [appSelections, setAppSelections] = React.useState([]);
  const [nameFilter, setNameFilter] = React.useState('');
  const hasFilters = appSelections.length > 0 || nameFilter;
  const selectLabelId = 'filter-application';
  const selectPlaceholder = 'Filter by application';

  const selectedNames = selectedRoles.map((role) => role.display_name);
  const filteredRows = rows
    .filter((row) =>
      appSelections.length > 0
        ? row.applications.find((app) => appSelections.includes(app))
        : true
    )
    .filter((row) => row.name.toLowerCase().includes(nameFilter))
    .filter((row) =>
      isReadOnly ? selectedNames.includes(row.display_name) : true
    );

  // Pagination
  const [page, setPage] = React.useState(1);
  const [perPage, setPerPage] = React.useState(10);
  const AccessRequestsPagination = ({ id }) => (
    <Pagination
      itemCount={filteredRows.length}
      perPage={perPage}
      page={page}
      onSetPage={(_ev, pageNumber) => setPage(pageNumber)}
      id={'access-requests-roles-table-pagination-' + id}
      variant={id}
      onPerPageSelect={(_ev, perPage) => {
        setPage(1);
        setPerPage(perPage);
      }}
      isCompact={id === 'top'}
    />
  );
  AccessRequestsPagination.propTypes = {
    id: PropTypes.string,
  };
  const pagedRows = filteredRows
    .sort((a, b) => {
      if (typeof a[activeSortIndex] === 'number') {
        // numeric sort
        if (activeSortDirection === 'asc') {
          return a[activeSortIndex] - b[activeSortIndex];
        }

        return b[activeSortIndex] - a[activeSortIndex];
      } else {
        // string sort
        if (activeSortDirection === 'asc') {
          return (a[activeSortIndex] + '').localeCompare(b[activeSortIndex]);
        }

        return (b[activeSortIndex] + '').localeCompare(a[activeSortIndex]);
      }
    })
    .slice((page - 1) * perPage, page * perPage);

  // Selecting
  const [isBulkSelectOpen, setIsBulkSelectOpen] = React.useState(false);
  const anySelected = selectedRoles.length > 0;
  const someChecked = anySelected ? null : false;
  const isChecked =
    selectedRoles.length === filteredRows.length && selectedRoles.length > 0
      ? true
      : someChecked;
  const onSelect = (_ev, isSelected, rowId) => {
    const changed = pagedRows[rowId].display_name;
    if (isSelected) {
      setSelectedRoles(selectedRoles.concat(changed));
    } else {
      setSelectedRoles(selectedRoles.filter((role) => role !== changed));
    }
  };

  const onSelectAll = (_ev, isSelected) => {
    if (isSelected) {
      setSelectedRoles(filteredRows.map((row) => row.display_name));
    } else {
      setSelectedRoles([]);
    }
  };

  const clearFiltersButton = (
    <Button
      variant="link"
      onClick={() => {
        setAppSelections([]);
        setNameFilter('');
      }}
    >
      Clear filters
    </Button>
  );
  const roleToolbar = isReadOnly ? null : (
    <Toolbar id="access-requests-roles-table-toolbar">
      <ToolbarContent>
        <ToolbarItem>
          <Dropdown
            onSelect={() => setIsBulkSelectOpen(!isBulkSelectOpen)}
            position="left"
            toggle={
              <DropdownToggle
                splitButtonItems={[
                  <DropdownToggleCheckbox
                    key="a"
                    id="example-checkbox-2"
                    aria-label={anySelected ? 'Deselect all' : 'Select all'}
                    isChecked={isChecked}
                    onClick={() => onSelectAll(null, !anySelected)}
                  />,
                ]}
                onToggle={(isOpen) => setIsBulkSelectOpen(isOpen)}
                isDisabled={rows.length === 0}
              >
                {selectedRoles.length !== 0 && (
                  <React.Fragment>
                    {selectedRoles.length} selected
                  </React.Fragment>
                )}
              </DropdownToggle>
            }
            isOpen={isBulkSelectOpen}
            dropdownItems={[
              <DropdownItem key="0" onClick={() => onSelectAll(null, false)}>
                Select none (0 items)
              </DropdownItem>,
              <DropdownItem
                key="1"
                onClick={() =>
                  setSelectedRoles(
                    selectedRoles.concat(pagedRows.map((r) => r.display_name))
                  )
                }
              >
                Select page ({Math.min(pagedRows.length, perPage)} items)
              </DropdownItem>,
              <DropdownItem key="2" onClick={() => onSelectAll(null, true)}>
                Select all ({filteredRows.length} items)
              </DropdownItem>,
            ]}
          />
        </ToolbarItem>
        <ToolbarItem>
          <InputGroup>
            <Dropdown
              isOpen={isDropdownOpen}
              onSelect={(ev) => {
                setIsDropdownOpen(false);
                setFilterColumn(ev.target.value);
                setIsSelectOpen(false);
              }}
              toggle={
                <DropdownToggle
                  onToggle={(isOpen) => setIsDropdownOpen(isOpen)}
                >
                  <FilterIcon /> {filterColumn}
                </DropdownToggle>
              }
              dropdownItems={['Role name', 'Application'].map((colName) => (
                // Filterable columns are RequestID, AccountID, and Status
                <DropdownItem key={colName} value={colName} component="button">
                  {capitalize(colName)}
                </DropdownItem>
              ))}
            />
            {filterColumn === 'Application' ? (
              <React.Fragment>
                <span id={selectLabelId} hidden>
                  {selectPlaceholder}
                </span>
                <Select
                  aria-labelledby={selectLabelId}
                  variant="checkbox"
                  aria-label="Select applications"
                  onToggle={(isOpen) => setIsSelectOpen(isOpen)}
                  onSelect={(_ev, selection) => {
                    if (appSelections.includes(selection)) {
                      setAppSelections(
                        appSelections.filter((s) => s !== selection)
                      );
                    } else {
                      setAppSelections([...appSelections, selection]);
                    }
                  }}
                  isOpen={isSelectOpen}
                  selections={appSelections}
                  isCheckboxSelectionBadgeHidden
                  placeholderText={selectPlaceholder}
                  style={{ maxHeight: '400px', overflowY: 'auto' }}
                >
                  {applications.map((app) => (
                    <SelectOption key={app} value={app}>
                      {capitalize(app.replace(/-/g, ' '))}
                    </SelectOption>
                  ))}
                </Select>
              </React.Fragment>
            ) : (
              <TextInput
                name="rolesSearch"
                id="rolesSearch"
                type="search"
                iconVariant="search"
                aria-label="Search input"
                placeholder="Filter by role name"
                value={nameFilter}
                onChange={(val) => setNameFilter(val)}
              />
            )}
          </InputGroup>
        </ToolbarItem>
        <ToolbarItem variant="pagination" align={{ default: 'alignRight' }}>
          <AccessRequestsPagination id="top" />
        </ToolbarItem>
      </ToolbarContent>
      {hasFilters && (
        <ToolbarContent>
          {nameFilter && (
            <ChipGroup categoryName="Role name">
              <Chip onClick={() => setNameFilter('')}>{nameFilter}</Chip>
            </ChipGroup>
          )}
          {appSelections.length > 0 && (
            <ChipGroup categoryName="Status">
              {appSelections.map((status) => (
                <Chip
                  key={status}
                  onClick={() =>
                    setAppSelections(appSelections.filter((s) => s !== status))
                  }
                >
                  {status}
                </Chip>
              ))}
            </ChipGroup>
          )}
          {clearFiltersButton}
        </ToolbarContent>
      )}
    </Toolbar>
  );

  const expandedColumns = ['Application', 'Resource type', 'Operation'];
  const dispatch = useDispatch();
  const onExpand = (row) => {
    row.isExpanded = !row.isExpanded;
    setRows([...rows]);
    if (!row.access) {
      apiInstance
        .get(`${API_BASE}/roles/${row.uuid}/`, {
          headers: { Accept: 'application/json' },
        })
        .then((res) => {
          row.access = res.access.map((a) => a.permission.split(':'));
          setRows([...rows]);
        })
        .catch((err) =>
          dispatch(
            addNotification({
              variant: 'danger',
              title: `Could not fetch permission list for ${row.name}.`,
              description: err.message,
            })
          )
        );
    }
  };
  const roleTable = (
    <TableComposable aria-label="My user access roles" variant="compact">
      <Thead>
        <Tr>
          {!isReadOnly && <Th />}
          <Th
            width={30}
            sort={{
              sortBy: {
                index: activeSortIndex,
                direction: activeSortDirection,
              },
              onSort,
              columnIndex: 'name',
            }}
          >
            {columns[0]}
          </Th>
          <Th
            width={50}
            sort={{
              sortBy: {
                index: activeSortIndex,
                direction: activeSortDirection,
              },
              onSort,
              columnIndex: 'description',
            }}
          >
            {columns[1]}
          </Th>
          <Th
            width={10}
            sort={{
              sortBy: {
                index: activeSortIndex,
                direction: activeSortDirection,
              },
              onSort,
              columnIndex: 'permissions',
            }}
            modifier="nowrap"
          >
            {columns[2]}
          </Th>
        </Tr>
      </Thead>
      {rows.length === 0 &&
        [...Array(perPage).keys()].map((i) => (
          <Tbody key={i}>
            <Tr>
              {!isReadOnly && <Td />}
              {columns.map((col, key) => (
                <Td dataLabel={col} key={key}>
                  <div
                    style={{ height: '22px' }}
                    className="ins-c-skeleton ins-c-skeleton__md"
                  >
                    {' '}
                  </div>
                </Td>
              ))}
            </Tr>
          </Tbody>
        ))}
      {pagedRows.map((row, rowIndex) => (
        <Tbody key={rowIndex}>
          <Tr>
            {!isReadOnly && (
              <Td
                select={{
                  rowIndex,
                  onSelect,
                  isSelected: selectedRoles.find((r) => r === row.display_name),
                }}
              />
            )}
            <Td dataLabel={columns[0]}>{row.display_name}</Td>
            <Td dataLabel={columns[1]} className="pf-m-truncate">
              <Tooltip entryDelay={1000} content={row.description}>
                <span className="pf-m-truncate pf-c-table__text">
                  {row.description}
                </span>
              </Tooltip>
            </Td>
            <Td
              dataLabel={columns[2]}
              className={css(
                'pf-c-table__compound-expansion-toggle',
                row.isExpanded && 'pf-m-expanded'
              )}
            >
              <button
                type="button"
                className="pf-c-table__button"
                onClick={() => onExpand(row)}
              >
                {row.permissions}
              </button>
            </Td>
          </Tr>
          <Tr isExpanded={row.isExpanded} borders={false}>
            {!isReadOnly && <Td />}
            <Td className="pf-u-p-0" colSpan={3}>
              <TableComposable isCompact className="pf-m-no-border-rows">
                <Thead>
                  <Tr>
                    {expandedColumns.map((col) => (
                      <Th key={col}>{col}</Th>
                    ))}
                  </Tr>
                </Thead>
                <Tbody>
                  {Array.isArray(row.access)
                    ? row.access.map((permissions) => (
                        <Tr key={permissions.join(':')}>
                          <Td dataLabel={expandedColumns[0]}>
                            {permissions[0]}
                          </Td>
                          <Td dataLabel={expandedColumns[1]}>
                            {permissions[1]}
                          </Td>
                          <Td dataLabel={expandedColumns[2]}>
                            {permissions[2]}
                          </Td>
                        </Tr>
                      ))
                    : [...Array(row.permissions).keys()].map((i) => (
                        <Tr key={i}>
                          {expandedColumns.map((val) => (
                            <Td key={val} dataLabel={val}>
                              <div
                                style={{ height: '22px' }}
                                className="ins-c-skeleton ins-c-skeleton__sm"
                              >
                                {' '}
                              </div>
                            </Td>
                          ))}
                        </Tr>
                      ))}
                </Tbody>
              </TableComposable>
            </Td>
          </Tr>
        </Tbody>
      ))}
      {pagedRows.length === 0 && hasFilters && (
        <Tr>
          <Td colSpan={columns.length}>
            <EmptyState variant="small">
              <EmptyStateIcon icon={SearchIcon} />
              <Title headingLevel="h2" size="lg">
                No matching requests found
              </Title>
              <EmptyStateBody>
                No results match the filter criteria. Remove all filters or
                clear all filters to show results.
              </EmptyStateBody>
              {clearFiltersButton}
            </EmptyState>
          </Td>
        </Tr>
      )}
    </TableComposable>
  );

  return (
    <React.Fragment>
      {!isReadOnly && (
        <React.Fragment>
          <Title headingLevel="h2">Select roles</Title>
          <p>Select the roles you would like access to.</p>
        </React.Fragment>
      )}
      {roleToolbar}
      {roleTable}
      {isReadOnly && <AccessRequestsPagination id="bottom" />}
    </React.Fragment>
  );
}
Example #9
Source File: FilterInput.js    From edge-frontend with Apache License 2.0 4 votes vote down vote up
FilterInput = ({ filterValues, setFilterValues, input }) => {
  const selectedFilter = filterValues.find((filter) => filter.label === input);
  const [isOpen, setIsOpen] = useState(false);

  const handleFilterChange = () => (value, checkboxValue) => {
    setFilterValues((prevState) => {
      const selectedIndex = prevState.findIndex(
        (filter) => filter.label === selectedFilter.label
      );
      const checkedType = prevState.find(
        (filter) => filter.label === selectedFilter.label
      );
      const checkboxIndex =
        selectedFilter.type === 'checkbox'
          ? checkedType.value.findIndex((i) => i.option === checkboxValue)
          : 0;
      const newValueArray = Object.values({
        ...checkedType.value,
        [checkboxIndex]: {
          ...checkedType.value[checkboxIndex],
          isChecked: !checkedType?.value[checkboxIndex]?.isChecked,
        },
      });
      const newTextValue = value;

      return Object.values({
        ...prevState,
        [selectedIndex]: {
          ...prevState[selectedIndex],
          value:
            selectedFilter.type === 'checkbox' ? newValueArray : newTextValue,
        },
      });
    });
  };

  const handleDeleteTextInput = () => {
    const filterLabelIndex = filterValues.findIndex(
      (value) => value.type === 'text'
    );
    setFilterValues((prevState) => {
      const changedValue = prevState[filterLabelIndex];
      if (changedValue.type === 'text') {
        return [
          ...prevState.slice(0, filterLabelIndex),
          { ...prevState[filterLabelIndex], value: '' },
          ...prevState.slice(filterLabelIndex + 1, prevState.length),
        ];
      }
      return prevState;
    });
  };

  if (selectedFilter.type === 'text') {
    return (
      <ToolbarItem data-testid="filter-input-testid">
        <InputGroup>
          <SearchInput
            name="textInput1"
            id="textInput1"
            type="search"
            aria-label={`Select input for ${selectedFilter.label.toLowerCase()}`}
            placeholder={`Filter by ${selectedFilter.label.toLowerCase()}`}
            onChange={debounce(handleFilterChange(), 500)}
            onClear={handleDeleteTextInput}
            value={filterValues.find((filter) => filter.type === 'text').value}
          />
        </InputGroup>
      </ToolbarItem>
    );
  }

  if (selectedFilter.type === 'checkbox') {
    return (
      <ToolbarItem data-testid="filter-input-testid">
        <InputGroup>
          <Select
            variant="checkbox"
            aria-label={`Select input for ${selectedFilter.label.toLowerCase()}`}
            width="11rem"
            placeholderText={`Filter by ${selectedFilter.label.toLowerCase()}`}
            isCheckboxSelectionBadgeHidden
            onToggle={() => setIsOpen((prevState) => !prevState)}
            onSelect={handleFilterChange()}
            selections={selectedFilter.value
              .filter((value) => value.isChecked == true)
              .map((arr) => arr.option)}
            isOpen={isOpen}
          >
            {selectedFilter.value.map((filter, index) => (
              <SelectOption
                key={index}
                value={filter.option}
                isChecked={filter.isChecked}
              />
            ))}
          </Select>
        </InputGroup>
      </ToolbarItem>
    );
  }
}
Example #10
Source File: Services.js    From sed-frontend with Apache License 2.0 4 votes vote down vote up
Services = ({
  defaults,
  setConfirmChangesOpen,
  onChange,
  isEditing,
  setIsEditing,
}) => {
  const initState = {
    enableCloudConnector: {
      value: defaults.enableCloudConnector,
      isDisabled: false,
    },
    useOpenSCAP: { value: defaults.useOpenSCAP, isDisabled: false },
  };
  const [formState, setFormState] = useState(initState);
  const [madeChanges, setMadeChanges] = useState(false);

  const { hasAccess, isLoading } = usePermissions(
    '',
    [
      'config-manager:activation_keys:*',
      'config-manager:state:read',
      'config-manager:state:write',
      'config-manager:state-changes:read',
      'inventory:*:read',
      'playbook-dispatcher:run:read',
    ],
    false,
    true
  );

  const cancelEditing = () => {
    setFormState(initState);
    setIsEditing(false);
  };

  useEffect(() => {
    setMadeChanges(
      formState.useOpenSCAP.value !== defaults.useOpenSCAP ||
        formState.enableCloudConnector.value != defaults.enableCloudConnector
    );
    onChange({
      useOpenSCAP: formState.useOpenSCAP.value,
      enableCloudConnector: formState.enableCloudConnector.value,
    });
  }, [formState]);

  const getStatusIcon = (row) => {
    if (formState[row.id].value) {
      return (
        <Flex style={{ color: 'var(--pf-global--success-color--200)' }}>
          <FlexItem spacer={{ default: 'spacerXs' }}>
            <CheckCircleIcon />
          </FlexItem>
          <FlexItem className="status">
            <b>Enabled</b>
          </FlexItem>
        </Flex>
      );
    }
    return (
      <Flex style={{ color: 'var(--pf-global--default-color--300)' }}>
        <FlexItem spacer={{ default: 'spacerXs' }}>
          <BanIcon />
        </FlexItem>
        <FlexItem className="status">
          <b>Disabled</b>
        </FlexItem>
      </Flex>
    );
  };

  return (
    <Stack hasGutter className="pf-u-p-md">
      <StackItem>
        <Toolbar id="toolbar-items">
          <ToolbarContent>
            {!isEditing && (
              <ToolbarItem>
                {!hasAccess ? (
                  <Tooltip
                    content={
                      <div>
                        To perform this action, you must be granted the
                        &quot;System Administrator&quot; role by your
                        Organization Administrator in your Setting&apos;s User
                        Access area.
                      </div>
                    }
                  >
                    {changeSettingsButton(isLoading, hasAccess, setIsEditing)}
                  </Tooltip>
                ) : (
                  changeSettingsButton(isLoading, hasAccess, setIsEditing)
                )}
              </ToolbarItem>
            )}
            {isEditing && (
              <>
                <ToolbarItem>
                  <Button
                    ouiaId="primary-save-button"
                    onClick={() => setConfirmChangesOpen(true)}
                    isDisabled={!madeChanges}
                  >
                    Save changes
                  </Button>
                </ToolbarItem>
                <ToolbarItem>
                  <Button
                    ouiaId="secondary-cancel-button"
                    onClick={() => cancelEditing()}
                    variant="secondary"
                  >
                    Cancel
                  </Button>
                </ToolbarItem>
                <ToolbarItem>
                  <Alert
                    variant="info"
                    isInline
                    isPlain
                    title="Changes will affect all systems connected with Red Hat connector"
                  />
                </ToolbarItem>
              </>
            )}
          </ToolbarContent>
        </Toolbar>
      </StackItem>
      <StackItem>
        <TableComposable aria-label="Settings table">
          <Thead>
            <Tr>
              <Th>Permission</Th>
              <Th>Status</Th>
            </Tr>
          </Thead>
          <Tbody>
            {permissions.map((row) => (
              <Tr key={row.name}>
                <Td
                  dataLabel="Permission"
                  width={80}
                  style={row.secondary && { paddingLeft: 70, fontSize: 14 }}
                >
                  <Stack>
                    <StackItem>
                      <Flex>
                        <FlexItem>
                          <b>{row.name}</b>
                        </FlexItem>
                        {row.additionalInfo && (
                          <FlexItem
                            style={{ color: 'var(--pf-global--Color--100)' }}
                          >
                            <i>{row.additionalInfo}</i>
                          </FlexItem>
                        )}
                      </Flex>
                    </StackItem>
                    <StackItem style={{ fontSize: 14 }}>
                      {row.description}
                    </StackItem>
                    {row.links && (
                      <StackItem className="stack-item">
                        <Flex>
                          {row.links.map((link) => (
                            <FlexItem key={link.name}>
                              <a
                                href={link.link}
                                target="_blank"
                                rel="noopener noreferrer"
                              >
                                {link.name}
                                <ExternalLinkAltIcon className="pf-u-ml-sm" />
                              </a>
                            </FlexItem>
                          ))}
                        </Flex>
                      </StackItem>
                    )}
                  </Stack>
                </Td>
                {!isEditing && <Td dataLabel="Status">{getStatusIcon(row)}</Td>}
                {isEditing && (
                  <Td dataLabel="Status">
                    <ToggleGroup aria-label="Default with single selectable">
                      <ToggleGroupItem
                        text="Enabled"
                        isSelected={formState[row.id].value}
                        onChange={() =>
                          setFormState({
                            ...formState,
                            [row.id]: { ...formState[row.id], value: true },
                          })
                        }
                        isDisabled={formState[row.id].isDisabled}
                      />
                      <ToggleGroupItem
                        text="Disabled"
                        isSelected={!formState[row.id].value}
                        onChange={() =>
                          setFormState({
                            ...formState,
                            [row.id]: { ...formState[row.id], value: false },
                          })
                        }
                        isDisabled={formState[row.id].isDisabled}
                      />
                    </ToggleGroup>
                  </Td>
                )}
              </Tr>
            ))}
          </Tbody>
        </TableComposable>
      </StackItem>
    </Stack>
  );
}