Commit 784b2426 authored by Stefan Probst's avatar Stefan Probst
Browse files

feat: add combobox popover empty collection placeholder

parent 13771c01
......@@ -38,6 +38,7 @@ export interface ComboBoxProps<T>
// loadingState?: 'loading'
shouldFocusWrap?: boolean
allowsEmptyCollection?: boolean
emptyCollectionPlaceholder?: string
hideSelectionIcon?: boolean
hideButton?: boolean
/** @default "text" */
......@@ -81,11 +82,16 @@ export function ComboBox<T extends object>(
const { errorMessageProps, fieldProps } = useErrorMessage(props)
const { buttonProps } = useButton(triggerProps, triggerRef)
const placeholder =
props.allowsEmptyCollection === true
? props.emptyCollectionPlaceholder ?? 'No results'
: undefined
/**
* When `items` are populated async, `useComboBoxState` does not correctly update
* the `inputValue`, because it only tracks changes to `selectedKey`, but not
* changes to `selectedItem`, which will be `null` initially, and changes once
* the `items` have been loaded.
* When `items` are populated async, `useComboBoxState` does not correctly
* update the `inputValue` with the selected item's label, because it only
* tracks changes to `selectedKey`, but not changes to `selectedItem`, which
* will be `null` initially, and changes once the `items` have been loaded.
*
* @see https://github.com/adobe/react-spectrum/issues/1645
*/
......@@ -215,6 +221,7 @@ export function ComboBox<T extends object>(
state={state}
isDisabled={props.isDisabled}
isLoading={props.isLoading}
placeholder={placeholder}
shouldFocusWrap={props.shouldFocusWrap}
variant={props.variant}
hideSelectionIcon={
......@@ -234,6 +241,7 @@ interface ListBoxProps<T> {
menuProps: HTMLAttributes<HTMLElement>
isDisabled?: boolean
isLoading?: boolean
placeholder?: string
shouldFocusWrap?: boolean
/** @default "default" */
variant?: 'default' | 'search' | 'form'
......@@ -276,6 +284,7 @@ function ListBox<T extends object>(props: ListBoxProps<T>): JSX.Element {
state={state}
isDisabled={props.isDisabled}
isLoading={props.isLoading}
placeholder={props.placeholder}
variant={props.variant}
shouldSelectOnPressUp
shouldFocusOnHover
......
......@@ -21,6 +21,7 @@ export interface ListBoxProps<T>
Validation,
LabelableProps {
isDisabled?: boolean
placeholder?: string
validationMessage?: ReactNode
/** @default "icon" */
necessityIndicator?: NecessityIndicator
......@@ -56,6 +57,7 @@ export function ListBox<T extends object>(props: ListBoxProps<T>): JSX.Element {
state={state}
isDisabled={props.isDisabled}
isLoading={props.isLoading}
placeholder={props.placeholder}
variant={props.variant}
/>
</Field>
......
......@@ -16,6 +16,7 @@ export interface ListBoxBaseProps<T> extends AsyncLoadable {
state: ListState<T>
listBoxRef: RefObject<HTMLUListElement>
isDisabled?: boolean
placeholder?: string
shouldSelectOnPressUp?: boolean
shouldFocusOnHover?: boolean
shouldUseVirtualFocus?: boolean
......@@ -39,6 +40,8 @@ export function ListBoxBase<T extends object>(
const isLoading = props.isLoading === true
const items = Array.from(state.collection)
const placeholder =
state.collection.size === 0 ? props.placeholder : undefined
const variant = props.variant ?? 'default'
......@@ -48,6 +51,7 @@ export function ListBoxBase<T extends object>(
'py-2 max-h-64 border border-gray-300 rounded overflow-x-hidden overflow-y-auto flex flex-col bg-white focus:outline-none',
variant === 'search' ? '' : '',
),
placeholder: 'italic',
loading: 'inline-flex items-center justify-center py-2 text-secondary-600',
spinner: 'w-4 h-4',
}
......@@ -97,6 +101,11 @@ export function ListBoxBase<T extends object>(
return null
})}
{placeholder !== undefined ? (
<li className={styles.placeholder} role="none">
{placeholder}
</li>
) : null}
{isLoading ? (
<div className={styles.loading}>
<ProgressSpinner className={styles.spinner} />
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment