Skip to content

Commit

Permalink
Merge pull request #2610 from 3scale/2.11/async_select_with_modal
Browse files Browse the repository at this point in the history
Select with Modal with Async pagination
  • Loading branch information
josemigallas committed Sep 22, 2021
2 parents 094ff7a + d9dcae8 commit 36c6734
Show file tree
Hide file tree
Showing 26 changed files with 831 additions and 343 deletions.
10 changes: 6 additions & 4 deletions app/javascript/src/BackendApis/components/BackendSelect.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const BackendSelect = ({ backend, backends, onSelect, onCreateNewBackend, error

return (
<>
{/* $FlowFixMe[prop-missing] Implement async pagination */}
<SelectWithModal
label="Backend"
fieldId="backend_api_config_backend_api_id"
Expand All @@ -35,14 +36,15 @@ const BackendSelect = ({ backend, backends, onSelect, onCreateNewBackend, error
// $FlowIssue[incompatible-type] backend is compatible with null
item={backend}
items={backends.map(b => ({ ...b, description: b.privateEndpoint }))}
itemsCount={backends.length}
cells={cells}
helperTextInvalid={error}
isValid={!error}
modalTitle="Select a Backend"
// $FlowIssue[incompatible-type] It should not complain since Record.id has union "number | string"
onSelect={onSelect}
header="Most recently created Backends"
footer="View all Backends"
title="Select a Backend"
placeholder="Select a Backend"
footerLabel="View all Backends"
helperTextInvalid={error}
/>
<Button
variant="link"
Expand Down
124 changes: 124 additions & 0 deletions app/javascript/src/Common/components/FancySelect.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// @flow

import * as React from 'react'

import {
FormGroup,
Select,
SelectVariant
} from '@patternfly/react-core'
import {
handleOnFilter,
toSelectOption,
toSelectOptionObject,
SelectOptionObject
} from 'utilities'

import type { Record } from 'utilities'

import './FancySelect.scss'

type Props<T: Record> = {
item: T | null,
items: T[],
onSelect: (T | null) => void,
label: string,
id: string,
header: string,
isDisabled?: boolean,
name?: string,
helperText?: React.Node,
helperTextInvalid?: string,
placeholderText?: string,
footer?: {
label: string,
onClick: () => void
}
}

const emptyItem = { id: -1, name: 'No results found', disabled: true, privateEndpoint: '' }
const FOOTER_ID = 'footer_id'

const FancySelect = <T: Record>({
item,
items,
onSelect,
label,
id,
header,
isDisabled,
name,
helperText,
helperTextInvalid,
placeholderText,
footer
}: Props<T>): React.Node => {
const [expanded, setExpanded] = React.useState(false)

const headerItem = { id: 'header', name: header, disabled: true, className: 'pf-c-select__menu-item--group-name' }
// TODO: Remove after upgrading @patternfly/react-core, see https://www.patternfly.org/v4/components/select#view-more
const footerItem = footer && { id: FOOTER_ID, name: footer.label, className: 'pf-c-select__menu-item--sticky-footer' }

const handleOnSelect = (_e, option: SelectOptionObject) => {
setExpanded(false)

if (option.id === FOOTER_ID) {
// $FlowIgnore[incompatible-use] safe to assume onClick is defined at this point
footer.onClick()
} else {
const selectedBackend = items.find(b => String(b.id) === option.id)

if (selectedBackend) {
onSelect(selectedBackend)
}
}
}

const getSelectOptionsForItems = (items: Array<T>) => {
const selectItems = [headerItem]

if (items.length === 0) {
selectItems.push(emptyItem)
} else {
selectItems.push(...items.map(i => ({ ...i, className: 'pf-c-select__menu-item-description' })))
}

if (footerItem) {
selectItems.push(footerItem)
}

return selectItems.map(toSelectOption)
}

return (
<FormGroup
isRequired
label={label}
fieldId={id}
helperText={helperText}
helperTextInvalid={helperTextInvalid}
isValid={!helperTextInvalid}
>
{name && item && <input type="hidden" name={name} value={item.id} />}
<Select
variant={SelectVariant.typeahead}
selections={item && toSelectOptionObject(item)}
onToggle={() => setExpanded(!expanded)}
onSelect={handleOnSelect}
isExpanded={expanded}
isDisabled={isDisabled}
onClear={() => onSelect(null)}
aria-labelledby={id}
className={footer ? 'pf-c-select__menu--with-fixed-link' : undefined}
isGrouped
// $FlowIgnore[incompatible-call] yes it is
onFilter={handleOnFilter(items, getSelectOptionsForItems)}
placeholderText={placeholderText}
>
{getSelectOptionsForItems(items)}
</Select>
</FormGroup>
)
}

export { FancySelect }
18 changes: 18 additions & 0 deletions app/javascript/src/Common/components/FancySelect.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// For a select box with fixed link at the bottom, select the last li to be that sticky link
.pf-c-select__menu--with-fixed-link li:last-of-type {
background-color: var(--pf-global--BackgroundColor--100);
bottom: calc(-1 * var(--pf-global--spacer--sm));
padding-bottom: var(--pf-global--spacer--sm);
padding-top: var(--pf-global--spacer--xs);
position: sticky;

// Make the sticky link look like a link
.pf-c-select__menu-item {
color: var(--pf-global--link--Color);

&:hover {
background-color: var(--pf-global--BackgroundColor--100);
color: var(--pf-global--link--Color--hover);
}
}
}
38 changes: 38 additions & 0 deletions app/javascript/src/Common/components/NoMatchFound.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// @flow

import * as React from 'react'

import {
Button,
Title,
EmptyState,
EmptyStatePrimary,
EmptyStateIcon,
EmptyStateBody
} from '@patternfly/react-core'
import { SearchIcon } from '@patternfly/react-icons'

import './NoMatchFound.scss'

type Props = {
onClearFiltersClick?: () => void
}

const NoMatchFound = ({ onClearFiltersClick }: Props): React.Node => (
<EmptyState>
<EmptyStateIcon icon={SearchIcon} />
<Title size="lg" headingLevel="h4">
No results found
</Title>
<EmptyStateBody>
No results match the filter criteria. Clear all filters to show results.
</EmptyStateBody>
{onClearFiltersClick && (
<EmptyStatePrimary>
<Button variant="link" onClick={onClearFiltersClick}>Clear all filters</Button>
</EmptyStatePrimary>
)}
</EmptyState>
)

export { NoMatchFound }
1 change: 1 addition & 0 deletions app/javascript/src/Common/components/NoMatchFound.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@import '~@patternfly/patternfly/components/Title/title.css';
Loading

0 comments on commit 36c6734

Please sign in to comment.