New Select: Initial scaffolding (#89114)
* Initial scaffolding * Extend props from Input * Rename to Combobox * Use search icon * Remove use of SelectableValue * Remove unused import * Memoize
This commit is contained in:
@@ -66,6 +66,7 @@
|
||||
"classnames": "2.5.1",
|
||||
"d3": "7.9.0",
|
||||
"date-fns": "3.6.0",
|
||||
"downshift": "^9.0.6",
|
||||
"hoist-non-react-statics": "3.3.2",
|
||||
"i18next": "^23.0.0",
|
||||
"i18next-browser-languagedetector": "^7.0.2",
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { Meta, StoryFn } from '@storybook/react';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { Combobox } from './Combobox';
|
||||
|
||||
const meta: Meta<typeof Combobox> = {
|
||||
title: 'Forms/Combobox',
|
||||
component: Combobox,
|
||||
args: {
|
||||
loading: undefined,
|
||||
invalid: undefined,
|
||||
placeholder: 'Select an option...',
|
||||
options: [
|
||||
{ label: 'Apple', value: 'apple' },
|
||||
{ label: 'Banana', value: 'banana' },
|
||||
{ label: 'Carrot', value: 'carrot' },
|
||||
{ label: 'Dill', value: 'dill' },
|
||||
{ label: 'Eggplant', value: 'eggplant' },
|
||||
{ label: 'Fennel', value: 'fennel' },
|
||||
{ label: 'Grape', value: 'grape' },
|
||||
{ label: 'Honeydew', value: 'honeydew' },
|
||||
{ label: 'Iceberg Lettuce', value: 'iceberg-lettuce' },
|
||||
{ label: 'Jackfruit', value: 'jackfruit' },
|
||||
{ label: '1', value: 1 },
|
||||
{ label: '2', value: 2 },
|
||||
{ label: '3', value: 3 },
|
||||
],
|
||||
value: 'banana',
|
||||
},
|
||||
};
|
||||
|
||||
export const Basic: StoryFn<typeof Combobox> = (args) => {
|
||||
const [value, setValue] = useState(args.value);
|
||||
return (
|
||||
<Combobox
|
||||
{...args}
|
||||
value={value}
|
||||
onChange={(val) => {
|
||||
setValue(val.value);
|
||||
action('onChange')(val);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default meta;
|
||||
@@ -0,0 +1,64 @@
|
||||
import { useCombobox } from 'downshift';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
|
||||
import { Icon } from '../Icon/Icon';
|
||||
import { Input, Props as InputProps } from '../Input/Input';
|
||||
|
||||
type Value = string | number;
|
||||
type Option = {
|
||||
label: string;
|
||||
value: Value;
|
||||
};
|
||||
|
||||
interface ComboboxProps
|
||||
extends Omit<InputProps, 'width' | 'prefix' | 'suffix' | 'value' | 'addonBefore' | 'addonAfter' | 'onChange'> {
|
||||
onChange: (val: Option) => void;
|
||||
value: Value;
|
||||
options: Option[];
|
||||
}
|
||||
|
||||
function itemToString(item: Option | null) {
|
||||
return item?.label || '';
|
||||
}
|
||||
|
||||
function itemFilter(inputValue: string) {
|
||||
const lowerCasedInputValue = inputValue.toLowerCase();
|
||||
|
||||
return (item: Option) => {
|
||||
return (
|
||||
!inputValue ||
|
||||
item?.label?.toLowerCase().includes(lowerCasedInputValue) ||
|
||||
item?.value?.toString().toLowerCase().includes(lowerCasedInputValue)
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export const Combobox = ({ options, onChange, value, ...restProps }: ComboboxProps) => {
|
||||
const [items, setItems] = useState(options);
|
||||
const selectedItem = useMemo(() => options.find((option) => option.value === value) || null, [options, value]);
|
||||
|
||||
const { getInputProps, getMenuProps, getItemProps, isOpen } = useCombobox({
|
||||
items,
|
||||
itemToString,
|
||||
selectedItem,
|
||||
onInputValueChange: ({ inputValue }) => {
|
||||
setItems(options.filter(itemFilter(inputValue)));
|
||||
},
|
||||
onSelectedItemChange: ({ selectedItem }) => onChange(selectedItem),
|
||||
});
|
||||
return (
|
||||
<div>
|
||||
<Input suffix={<Icon name={isOpen ? 'search' : 'angle-down'} />} {...restProps} {...getInputProps()} />
|
||||
<ul {...getMenuProps()}>
|
||||
{isOpen &&
|
||||
items.map((item, index) => {
|
||||
return (
|
||||
<li key={item.value} {...getItemProps({ item, index })}>
|
||||
{item.label}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user