Skip to content

Commit

Permalink
feat(TypeaheadSelect) Add creation options to TypeaheadSelect
Browse files Browse the repository at this point in the history
  • Loading branch information
jeff-phillips-18 committed Jul 25, 2024
1 parent b7ac750 commit e3de113
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 22 deletions.
52 changes: 39 additions & 13 deletions packages/react-templates/src/components/Select/TypeaheadSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ export interface TypeaheadSelectProps extends Omit<SelectProps, 'toggle'> {
onClearSelection?: () => void;
/** Placeholder text for the select input. */
placeholder?: string;
/** Flag to indicate if the typeahead select allows new items */
isCreatable?: boolean;
/** Flag to indicate if create option should be at top of typeahead */
isCreateOptionOnTop?: boolean;
/** Message to display to create a new option */
createOptionMessage?: string | ((newValue: string) => string);
/** Message to display when no options are available. */
noOptionsAvailableMessage?: string;
/** Message to display when no options match the filter. */
Expand All @@ -62,6 +68,9 @@ export const TypeaheadSelectBase: React.FunctionComponent<TypeaheadSelectProps>
placeholder = 'Select an option',
noOptionsAvailableMessage = 'No options are available',
noOptionsFoundMessage = (filter) => `No results found for "${filter}"`,
isCreatable = false,
isCreateOptionOnTop = false,
createOptionMessage = (newValue) => `Create "${newValue}"`,
isDisabled,
toggleWidth,
toggleProps,
Expand Down Expand Up @@ -89,6 +98,20 @@ export const TypeaheadSelectBase: React.FunctionComponent<TypeaheadSelectProps>
String(option.content).toLowerCase().includes(filterValue.toLowerCase())
);

if (
isCreatable &&
filterValue &&
!selectOptions.find((o) => String(o.content).toLowerCase() === filterValue.toLowerCase())
) {
const createOption = {
content: typeof createOptionMessage === 'string' ? createOptionMessage : createOptionMessage(filterValue),
value: filterValue
};
newSelectOptions = isCreateOptionOnTop
? [createOption, ...newSelectOptions]
: [...newSelectOptions, createOption];
}

// When no options are found after filtering, display 'No results found'
if (!newSelectOptions.length) {
newSelectOptions = [
Expand All @@ -102,9 +125,7 @@ export const TypeaheadSelectBase: React.FunctionComponent<TypeaheadSelectProps>
}

// Open the menu when the input value changes and the new value is not empty
if (!isOpen) {
openMenu();
}
openMenu();
}

// When no options are available, display 'No options available'
Expand All @@ -119,7 +140,15 @@ export const TypeaheadSelectBase: React.FunctionComponent<TypeaheadSelectProps>
}

setSelectOptions(newSelectOptions);
}, [filterValue, initialOptions]);
}, [
filterValue,
initialOptions,
noOptionsFoundMessage,
isCreatable,
isCreateOptionOnTop,
createOptionMessage,
noOptionsAvailableMessage
]);

React.useEffect(() => {
const selectedOption = initialOptions.find((o) => o.selected);
Expand All @@ -138,8 +167,10 @@ export const TypeaheadSelectBase: React.FunctionComponent<TypeaheadSelectProps>
};

const openMenu = () => {
onToggle && onToggle(true);
setIsOpen(true);
if (!isOpen) {
onToggle && onToggle(true);
setIsOpen(true);
}
};

const closeMenu = () => {
Expand Down Expand Up @@ -191,9 +222,7 @@ export const TypeaheadSelectBase: React.FunctionComponent<TypeaheadSelectProps>
const handleMenuArrowKeys = (key: string) => {
let indexToFocus = 0;

if (!isOpen) {
openMenu();
}
openMenu();

if (selectOptions.every((option) => option.isDisabled)) {
return;
Expand Down Expand Up @@ -245,10 +274,7 @@ export const TypeaheadSelectBase: React.FunctionComponent<TypeaheadSelectProps>
selectOption(event, focusedItem);
}

if (!isOpen) {
onToggle && onToggle(true);
setIsOpen(true);
}
openMenu();

break;
case 'ArrowUp':
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import { TypeaheadSelect, TypeaheadSelectOption } from '@patternfly/react-templates';
import { Checkbox } from '@patternfly/react-core';

const Options = [
{ content: 'Alabama', value: 'option1' },
Expand All @@ -13,23 +14,52 @@ const Options = [
/* eslint-disable no-console */
export const SelectTypeaheadDemo: React.FunctionComponent = () => {
const [selected, setSelected] = React.useState<string | undefined>();
const [options, setOptions] = React.useState(Options);
const [isCreatable, setIsCreatable] = React.useState<boolean>(false);
const [isCreateOptionOnTop, setIsCreateOptionOnTop] = React.useState<boolean>(false);

const initialOptions = React.useMemo<TypeaheadSelectOption[]>(
() => Options.map((o) => ({ ...o, selected: o.value === selected })),
[selected]
() => options.map((o) => ({ ...o, selected: o.value === selected })),
[options, selected]
);

React.useEffect(() => {
console.log(`Selected: ${selected || 'none'}`);
}, [selected]);

return (
<TypeaheadSelect
initialOptions={initialOptions}
placeholder="Select a state"
noOptionsFoundMessage={(filter) => `No state was found for "${filter}"`}
onClearSelection={() => setSelected(undefined)}
onSelect={(_ev, selection) => setSelected(String(selection))}
/>
<>
<TypeaheadSelect
initialOptions={initialOptions}
placeholder="Select a state"
noOptionsFoundMessage={(filter) => `No state was found for "${filter}"`}
onClearSelection={() => setSelected(undefined)}
onSelect={(_ev, selection) => {
if (!options.find((o) => o.content === selection)) {
setOptions([...options, { content: String(selection), value: String(selection) }]);
}
setSelected(String(selection));
}}
isCreatable={isCreatable}
isCreateOptionOnTop={isCreateOptionOnTop}
/>
<Checkbox
className="pf-u-mt-sm"
label="isCreatable"
isChecked={isCreatable}
onChange={(_event, checked) => setIsCreatable(checked)}
aria-label="toggle creatable checkbox"
id="toggle-creatable-typeahead"
name="toggle-creatable-typeahead"
/>
<Checkbox
label="isCreateOptionOnTop"
isChecked={isCreateOptionOnTop}
onChange={(_event, checked) => setIsCreateOptionOnTop(checked)}
aria-label="toggle createOptionOnTop checkbox"
id="toggle-create-option-on-top-typeahead"
name="toggle-create-option-on-top-typeahead"
/>
</>
);
};

0 comments on commit e3de113

Please sign in to comment.