mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
MultiCombobox: Add Clear all
button (#99668)
This commit is contained in:
parent
c2b44a5da1
commit
2df05505db
@ -128,6 +128,32 @@ describe('MultiCombobox', () => {
|
||||
expect(await screen.findByText('d')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should remove value when clicking on the close icon of the pill', async () => {
|
||||
const options = [
|
||||
{ label: 'A', value: 'a' },
|
||||
{ label: 'B', value: 'b' },
|
||||
{ label: 'C', value: 'c' },
|
||||
];
|
||||
const onChange = jest.fn();
|
||||
render(<MultiCombobox width={200} options={options} value={['a', 'b', 'c']} onChange={onChange} />);
|
||||
const fistPillRemoveButton = await screen.findByRole('button', { name: 'Remove A' });
|
||||
await user.click(fistPillRemoveButton);
|
||||
expect(onChange).toHaveBeenCalledWith(options.filter((o) => o.value !== 'a'));
|
||||
});
|
||||
|
||||
it('should remove all selected items when clicking on clear all button', async () => {
|
||||
const options = [
|
||||
{ label: 'A', value: 'a' },
|
||||
{ label: 'B', value: 'b' },
|
||||
{ label: 'C', value: 'c' },
|
||||
];
|
||||
const onChange = jest.fn();
|
||||
render(<MultiCombobox width={200} options={options} value={['a', 'b', 'c']} onChange={onChange} isClearable />);
|
||||
const clearAllButton = await screen.findByTitle('Clear all');
|
||||
await user.click(clearAllButton);
|
||||
expect(onChange).toHaveBeenCalledWith([]);
|
||||
});
|
||||
|
||||
describe('all option', () => {
|
||||
it('should render all option', async () => {
|
||||
const options = [
|
||||
|
@ -6,6 +6,7 @@ import { useCallback, useMemo, useState } from 'react';
|
||||
import { useStyles2 } from '../../themes';
|
||||
import { t } from '../../utils/i18n';
|
||||
import { Checkbox } from '../Forms/Checkbox';
|
||||
import { Icon } from '../Icon/Icon';
|
||||
import { Box } from '../Layout/Box/Box';
|
||||
import { Stack } from '../Layout/Stack/Stack';
|
||||
import { Portal } from '../Portal/Portal';
|
||||
@ -36,7 +37,8 @@ interface MultiComboboxBaseProps<T extends string | number> extends Omit<Combobo
|
||||
export type MultiComboboxProps<T extends string | number> = MultiComboboxBaseProps<T> & AutoSizeConditionals;
|
||||
|
||||
export const MultiCombobox = <T extends string | number>(props: MultiComboboxProps<T>) => {
|
||||
const { placeholder, onChange, value, width, enableAllOption, invalid, disabled, minWidth, maxWidth } = props;
|
||||
const { placeholder, onChange, value, width, enableAllOption, invalid, disabled, minWidth, maxWidth, isClearable } =
|
||||
props;
|
||||
|
||||
const styles = useStyles2(getComboboxStyles);
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
@ -80,7 +82,7 @@ export const MultiCombobox = <T extends string | number>(props: MultiComboboxPro
|
||||
[selectedItems]
|
||||
);
|
||||
|
||||
const { getSelectedItemProps, getDropdownProps, setSelectedItems, addSelectedItem, removeSelectedItem } =
|
||||
const { getSelectedItemProps, getDropdownProps, setSelectedItems, addSelectedItem, removeSelectedItem, reset } =
|
||||
useMultipleSelection({
|
||||
selectedItems, // initally selected items,
|
||||
onStateChange: ({ type, selectedItems: newSelectedItems }) => {
|
||||
@ -91,6 +93,7 @@ export const MultiCombobox = <T extends string | number>(props: MultiComboboxPro
|
||||
case useMultipleSelection.stateChangeTypes.FunctionRemoveSelectedItem:
|
||||
case useMultipleSelection.stateChangeTypes.FunctionAddSelectedItem:
|
||||
case useMultipleSelection.stateChangeTypes.FunctionSetSelectedItems:
|
||||
case useMultipleSelection.stateChangeTypes.FunctionReset:
|
||||
// Unclear why newSelectedItems would be undefined, but this seems logical
|
||||
onChange(newSelectedItems ?? []);
|
||||
break;
|
||||
@ -220,7 +223,16 @@ export const MultiCombobox = <T extends string | number>(props: MultiComboboxPro
|
||||
});
|
||||
|
||||
const { inputRef: containerRef, floatingRef, floatStyles, scrollRef } = useComboboxFloat(options, isOpen);
|
||||
const multiStyles = useStyles2(getMultiComboboxStyles, isOpen, invalid, disabled, width, minWidth, maxWidth);
|
||||
const multiStyles = useStyles2(
|
||||
getMultiComboboxStyles,
|
||||
isOpen,
|
||||
invalid,
|
||||
disabled,
|
||||
width,
|
||||
minWidth,
|
||||
maxWidth,
|
||||
isClearable
|
||||
);
|
||||
|
||||
const virtualizerOptions = {
|
||||
count: options.length,
|
||||
@ -284,6 +296,24 @@ export const MultiCombobox = <T extends string | number>(props: MultiComboboxPro
|
||||
/>
|
||||
|
||||
<div className={multiStyles.suffix} ref={suffixMeasureRef} {...getToggleButtonProps()}>
|
||||
{isClearable && selectedItems.length > 0 && (
|
||||
<Icon
|
||||
name="times"
|
||||
className={styles.clear}
|
||||
title={t('multicombobox.clear.title', 'Clear all')}
|
||||
tabIndex={0}
|
||||
role="button"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
reset();
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
reset();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<SuffixIcon isLoading={loading || false} isOpen={isOpen} />
|
||||
</div>
|
||||
</span>
|
||||
|
@ -12,7 +12,8 @@ export const getMultiComboboxStyles = (
|
||||
disabled?: boolean,
|
||||
width?: number | 'auto',
|
||||
minWidth?: number,
|
||||
maxWidth?: number
|
||||
maxWidth?: number,
|
||||
isClearable?: boolean
|
||||
) => {
|
||||
const inputStyles = getInputStyles({ theme, invalid });
|
||||
const focusStyles = getFocusStyles(theme);
|
||||
@ -35,7 +36,7 @@ export const getMultiComboboxStyles = (
|
||||
width: '100%',
|
||||
gap: theme.spacing(0.5),
|
||||
padding: theme.spacing(0.5),
|
||||
paddingRight: 28, // Account for suffix
|
||||
paddingRight: isClearable ? theme.spacing(5) : 28, // Account for suffix
|
||||
'&:focus-within': {
|
||||
...focusStyles,
|
||||
},
|
||||
|
@ -2072,6 +2072,9 @@
|
||||
"all": {
|
||||
"title": "All",
|
||||
"title-filtered": "All (filtered)"
|
||||
},
|
||||
"clear": {
|
||||
"title": "Clear all"
|
||||
}
|
||||
},
|
||||
"nav": {
|
||||
|
@ -2072,6 +2072,9 @@
|
||||
"all": {
|
||||
"title": "Åľľ",
|
||||
"title-filtered": "Åľľ (ƒįľŧęřęđ)"
|
||||
},
|
||||
"clear": {
|
||||
"title": "Cľęäř äľľ"
|
||||
}
|
||||
},
|
||||
"nav": {
|
||||
|
Loading…
Reference in New Issue
Block a user