Zum Hauptinhalt springen

Combobox

Note: Es gibt veraltete Versionen dieser Komponente. Wechseln Sie zu einer Version, die älter als 10.0.x ist, um die entsprechende Dokumentation anzuzeigen.

Beispiele​

Im Abschnitt Verwendung finden Sie Informationen darüber, wie und wann Sie Combobox verwenden können. Auf den speziellen Registerkarten finden Sie Einzelheiten zu den von der Komponente bereitgestellten Funktionen.

Der Hook useFilteredOptions wird verwendet, um den Filtermechanismus basierend auf Benutzereingaben zu implementieren. Aus Gründen der Übersichtlichkeit und Einfachheit ist er jedoch in einigen Beispielen nicht enthalten. Genauere Informationen zu dem Hook finden Sie hier.

Note: Die meisten Funktionen und Eigenschaften der vier verfügbaren Dropdown-Komponenten unterscheiden sich bei der Verwendung nicht. Einige Beispiele aus anderen Komponenten können auch für Combobox relevant sein.

Kombinationsfeld, gefiltert nach Benutzereingabe​

export function ComboWithFilter() {
const CHARACTERS = [
{ id: '1', label: 'Anakin Skywalker' },
{ id: '2', label: 'Luke Skywalker' },
{ id: '3', label: 'Master Yoda' },
{ id: '4', label: 'Han Solo' },
];

const { filteredOptions, onSearch, resetFilter } =
useFilteredOptions(CHARACTERS);

return (
<Combobox
label="Select 1 option"
secondaryLabel="Options filtered by user input"
onSearch={onSearch}
onBlur={resetFilter}>
{filteredOptions.map(({ id, label }) => (
<ComboboxOption
key={id}
value={{ id, label }}
/>
))}
</Combobox>
);
}

Kombinationsfeld mit Initialwert​

Definieren Sie einen Initialwert für die Combobox:

export function ComboWithInitial() {
const CHARACTERS = [
{ id: '1', label: 'Anakin Skywalker' },
{ id: '2', label: 'Luke Skywalker' },
{ id: '3', label: 'Master Yoda' },
{ id: '4', label: 'Han Solo' },
];

const { filteredOptions, onSearch, resetFilter } =
useFilteredOptions(CHARACTERS);

return (
<Combobox
label="Select 1 option"
secondaryLabel="Options filtered by user input"
initialValue={CHARACTERS[2]}
onSearch={onSearch}
onBlur={resetFilter}>
{filteredOptions.map(({ id, label }) => (
<ComboboxOption
key={id}
value={{ id, label }}
/>
))}
</Combobox>
);
}

Deaktivierte Dropdown-Komponente oder Optionen​

Sie können jede der Dropdown-Komponenten vollständig deaktivieren, indem Sie die Eigenschaft disabled auf true setzen. Mit der Eigenschaft disabled der Komponenten SelectOption und ComboboxOption können Sie auch nur die speziellen Optionen deaktivieren. Wenn die gesamte Komponente deaktiviert ist, hat die Eigenschaft disabled der Optionen keine Auswirkungen.

Deaktivierte Dropdown-Komponente​

Beispiel für eine deaktivierte Dropdown-Komponente unter Verwendung der Combobox-Komponente.

<Combobox
label="Your favorite character"
initialValue={{ id: '1', label: 'Anakin' }}
disabled={true}>
<ComboboxOption
value={{
id: '1',
label: 'Anakin',
}}
/>
<ComboboxOption
value={{
id: '2',
label: 'Luke',
}}
/>
</Combobox>

Deaktivierte Optionen​

Beispiel für die Deaktivierung einiger Dropdown-Optionen unter Verwendung der Komponente Combobox.

export function ComboWithDisabled() {
const CHARACTERS = [
{ id: '1', label: 'Anakin Skywalker' },
{ id: '2', label: 'Luke Skywalker', disabled: true },
{ id: '3', label: 'Master Yoda' },
{ id: '4', label: 'Han Solo', disabled: true },
];

const { filteredOptions, onSearch, resetFilter } =
useFilteredOptions(CHARACTERS);

return (
<Combobox
label="Select 1 option"
secondaryLabel="Options filtered by user input"
onSearch={onSearch}
onBlur={resetFilter}>
{filteredOptions.map(({ id, label, disabled }) => (
<ComboboxOption
key={id}
value={{ id, label }}
disabled={disabled}
/>
))}
</Combobox>
);
}

DisplayOnly-Dropdown​

Durch Festlegen der Eigenschaft displayOnly auf true wird der Komponentenwert als Klartext angezeigt.

Hier ein Beispiel:

<Combobox
label="Your favorite characters (Combobox component)"
initialValue={{ id: '1', label: 'Anakin' }}
displayOnly={true}>
<ComboboxOption
value={{
id: '1',
label: 'Anakin',
}}
/>
</Combobox>

Kontrollierte Komponente​

Dieses Verhalten ist bei allen Dropdown-Komponenten gleich: Wenn die value-Eigenschaft festgelegt ist, kann der Benutzer die Komponente nicht direkt ändern. Stattdessen wird sie vollständig über die Implementierung durch Entwickler verwaltet.

Dieses Beispiel gilt für alle Dropdown-Komponenten:

export function ComboControlled() {
const [updatedValue, setNewValue] = useState(null);

const onChange = (e, newValue) => {
setNewValue(newValue);
};

const options = [
<ComboboxOption
value={{
id: '1',
label: 'Option 1',
}}
/>,
<ComboboxOption
value={{
id: '2',
label: 'Option 2',
}}
/>,
<ComboboxOption
value={{
id: '3',
label: 'Option 3',
}}
/>,
<ComboboxOption
value={{
id: '4',
label: 'Option 4',
}}
/>,
<ComboboxOption
value={{
id: '5',
label: 'Option 5',
}}
/>,
];

return (
<div>
<Combobox
label="Choose values"
secondaryLabel="This value will be passed to the list of options below"
onChange={onChange}>
{options}
</Combobox>
<br />
<Combobox
label="Changes with the above"
value={updatedValue}>
{options}
</Combobox>
</div>
);
}

Fokus auf die Dropdown-Komponente setzen​

Mit der Eigenschaft ref und den imperativen Handlern („focus“, „blur“ und „scrollIntoView“) ist es möglich, verschiedene native Aktionen auszuführen. Ein Beispiel ist, die Komponente zu fokussieren.

Dies ist ein Beispiel unter Verwendung der Komponente Combobox:

export function ComboRef() {
const selectRef = useRef(null);

const setFocus = (e) => {
selectRef?.current?.focus();
};

return (
<div>
<Button
label="Set focus on Combobox"
onClick={setFocus}></Button>
<Combobox
label="Get the focus from the button"
ref={selectRef}>
<ComboboxOption
value={{
id: '1',
label: 'Option 1',
}}
/>
<ComboboxOption
value={{
id: '2',
label: 'Option 2',
}}
/>
</Combobox>
</div>
);
}

Komponentenvalidierung​

In den Dropdown-Komponenten lässt sich der Validierungsprozess nicht durchführen, aber Sie können den Fehlerstatus und die anzuzeigenden Meldungen über die Eigenschaft stateMessages bearbeiten.

Die Logik der Statusmeldungen gilt für alle Dropdown-Komponenten. Nachfolgend ein Beispiel für MultipleSelect:

export function ComboValidation() {
const [validationMessages, setValidationMessages] = useState({});

const onChange = useCallback((e, newValue) => {
setValidationMessages({});

if (newValue && newValue.label !== 'Option 2') {
setValidationMessages({
error: ['You must select option number 2'],
});
}
}, []);

return (
<Combobox
label="Select option 2"
stateMessages={validationMessages}
onChange={onChange}>
<ComboboxOption
value={{
id: '1',
label: 'Option 1',
}}
/>
<ComboboxOption
value={{
id: '2',
label: 'Option 2',
}}
/>
<ComboboxOption
value={{
id: '3',
label: 'Option 3',
}}
/>
<ComboboxOption
value={{
id: '4',
label: 'Option 4',
}}
/>
<ComboboxOption
value={{
id: '5',
label: 'Option 5',
}}
/>
</Combobox>
);
}

Hook useFilteredOptions​

Beispiel-Implementierung​

Der Hook useFilteredOptions bietet eine grundlegende Datenverarbeitung für die Filterung.

import React, { useState } from 'react';
import { IntlMessageShape } from '@jutro/prop-types';
import { useTranslator } from '@jutro/locale';

type SelectValue = {
id: string;
label: IntlMessageShape;
[index: PropertyKey]: unknown;
};

/**
* @typedef {Object} FilterHookReturnObject
* @property {TValue[]} filteredOptions - array of filtered options
* @property {function} onSearch - function that takes the onSearch input event
* and filters the options based on it
* @property {function} resetFilter - used to reset the filtering for options,
* after using filteredOptions array resets to initial one
* @property {function} onAddNew - function that adds new value to the internal
* options array inside the hook and calls appendToSelection that is responsible
* for selecting this new value inside the dropdown
*/

/**
* Helper hook for filtering options based on user input.
*
* @param {TValue[]} initialOptions - initial list of options in component
* @param {function} createNewValue - function for constructing value object
* from string
* @param {function} appendToSelection - callback that is responsible for
* appending value to the controlled list of selected values
*
* @returns {FilterHookReturnObject} - helpers for filtering purposes:
* filtered options array, onAddNew and onSearch function to be supplied
* to component and resetFilter function
*/
export const useFilteredOptions = <TValue extends SelectValue = SelectValue>(
initialOptions: TValue[],
createNewValue?: (value: string) => TValue,
appendToSelection?: (value: TValue) => void
): {
filteredOptions: TValue[];
onSearch: (event: React.ChangeEvent<HTMLInputElement>) => void;
resetFilter: () => void;
onAddNew: (newValue: string) => void;
} => {
const translator = useTranslator();
const [query, setQuery] = useState('');
const [options, setOptions] = useState(initialOptions);

// Basic filtering. This part should be changed to cover your needs.
const filteredOptions = options.filter(val =>
translator(val.label)
.trim()
.toLowerCase()
.includes(query.trim().toLowerCase())
);

// Function that should be passed to the Combobox onSearch prop.
// It gets the value of the search term inside Combobox.
const onSearch = (event: React.ChangeEvent<HTMLInputElement>) => {
setQuery(event.target.value);
};

// Function that is responsible for clearing the query
// only when the Combobox is on active browser tab.
// Typically connected to Combobox onBlur.
const resetFilter = () => {
if (document.visibilityState === 'visible' && document.hasFocus()) {
setQuery('');
}
};

// Function responsible on adding new values to both
// internal state of filtered items and external
// controlled state value
const onAddNew = (newValue: string) => {
const valueTransformed = createNewValue?.(newValue);

setQuery('');

if (valueTransformed) {
// adds the value to the internal state
setOptions(prevOptions => [...prevOptions, valueTransformed]);
// adds the value to the controlled state
appendToSelection?.(valueTransformed);
}
};

return { filteredOptions, onSearch, resetFilter, onAddNew };
};

Beispiel-Implementierung mit asynchronen API-Aufrufen​

Asynchrone API-Aufrufe werden nicht innerhalb des Basis-Hooks useFilteredOptions verarbeitet. Zu diesem Zweck müssen Sie möglicherweise eine eigene Implementierung erstellen, die auf Ihre speziellen Anforderungen zugeschnitten ist. Dies ist ein Beispiel für einen benutzerdefinierten Hook für die Komponente Combobox:

import React, { useEffect, useState } from 'react';

import { Combobox, ComboboxOption } from '@jutro/components';
import { useTranslator } from '@jutro/locale';
import { IntlMessageShape } from '@jutro/prop-types';

type SelectValue = {
id: string;
label: IntlMessageShape;
[index: PropertyKey]: unknown;
};

const someAPICallback = async query => {
console.log('calling the API');

const CHARACTERS = [
{ id: '1', label: 'Anakin Skywalker' },
{ id: '2', label: 'Luke Skywalker' },
{ id: '3', label: 'Master Yoda' },
{ id: '4', label: 'Han Solo' },
];

const result = new Promise(resolve => {
setTimeout(
() =>
resolve(
CHARACTERS.filter(val =>
val.label
.trim()
.toLowerCase()
.includes(query.trim().toLowerCase())
)
),
3000
);
});

return result;
};

const useFilteredOptions = <TValue extends SelectValue = SelectValue>(): {
filteredOptions: TValue[];
onSearch: (event: React.ChangeEvent<HTMLInputElement>) => void;
resetFilter: () => void;
loading: boolean;
} => {
const [query, setQuery] = useState('');
const [filteredOptions, setFilteredOptions] = useState([]);
const [loading, setLoading] = useState(true);

useEffect(() => {
setLoading(true);

// This is a simple example only, API callback should be debounced
// to ensure the async operation does not overlap with the query change.
someAPICallback(query).then((opts: TValue[]) => {
setFilteredOptions(opts);
setLoading(false);
});
}, [query]);

// Function that should be passed to the Combobox onSearch prop.
// It gets the value of the search term inside Combobox.
const onSearch = (event: React.ChangeEvent<HTMLInputElement>) => {
setQuery(event.target.value);
};

// Function that is responsible for clearing the query
// only when the Combobox is on active browser tab.
// Typically connected to Combobox onBlur.
const resetFilter = () => {
if (document.visibilityState === 'visible' && document.hasFocus()) {
setQuery('');
}
};

return { filteredOptions, onSearch, resetFilter, loading };
};

export const Welcome: React.FC = () => {
const translator = useTranslator();
const { filteredOptions, onSearch, resetFilter, loading } =
useFilteredOptions();

return (
<Combobox
label="Select 1 option"
secondaryLabel="Options filtered by user input"
onSearch={onSearch}
onBlur={resetFilter}
>
{loading ? (
<span>{translator('Loading...')}</span>
) : (
filteredOptions.map(({ id, label }) => (
<ComboboxOption key={id} value={{ id, label }} />
))
)}
</Combobox>
);
};


Clear selected option​

By default, the Combobox component does not allow the user to clear a selected option. You can enable this feature and the ability to delete typed text using one of two available solutions.

Clearable input​

To enable clearing the input with a single click on the icon button, set the clearable property to true. The button appears when the option is selected, or any text is typed. It allows the user to unselect the selected option or delete the typed text.

This is the recommended solution for clearing the selected option in the Combobox component.

export function ComboClearable() {
return (
<Combobox
label="Select a character"
clearable={true}>
<ComboboxOption value={{ id: '1', label: 'Anakin Skywalker' }} />
<ComboboxOption value={{ id: '2', label: 'Luke Skywalker' }} />
<ComboboxOption value={{ id: '3', label: 'Master Yoda' }} />
<ComboboxOption value={{ id: '4', label: 'Han Solo' }} />
</Combobox>
);
}

Using placeholder option​

To enable clearing the selected option, use the placeholder and showPlaceholderAsOption properties. In this solution, the provided placeholder is available as the first dropdown option, allowing users to unselect the selected option or delete the typed text by choosing it.

You need to set these two properties as follows:

  • placeholder cannot be empty
  • showPlaceholderAsOption set to true

This solution avoids putting interactive elements within the dropdown. However, using placeholder text in the Combobox component is not recommended. Therefore, it is not the recommended solution for clearing the selected option in this component.

<Combobox
label="Your favorite character"
initialValue={{ id: '1', label: 'Anakin' }}
placeholder="Choose a character"
showPlaceholderAsOption>
<ComboboxOption
value={{
id: '1',
label: 'Anakin',
}}
/>
<ComboboxOption
value={{
id: '2',
label: 'Luke',
}}
/>
</Combobox>