diff --git a/packages/components/src/core/Autocomplete/components/AutocompleteMultiColumn/index.tsx b/packages/components/src/core/Autocomplete/components/AutocompleteMultiColumn/index.tsx index 864607312..8496c8c85 100644 --- a/packages/components/src/core/Autocomplete/components/AutocompleteMultiColumn/index.tsx +++ b/packages/components/src/core/Autocomplete/components/AutocompleteMultiColumn/index.tsx @@ -290,7 +290,12 @@ const AutocompleteMultiColumn = < } multiple={multiple} label={label} - InputBaseProps={InputBaseProps} + /** + * (masoudmanson): In a multi-column autocomplete, the search input for + * inner autocompletes should remain hidden and unfocused when the parent + * input is focused. This prevents blurring of the main search input. + */ + InputBaseProps={{ ...InputBaseProps, autoFocus: false }} popperOpen={popperOpen} inputValue={inputValue} PaperComponent={StyledPaper} diff --git a/packages/components/src/core/ComplexFilter/__storybook__/index.stories.tsx b/packages/components/src/core/ComplexFilter/__storybook__/index.stories.tsx index 234602c81..592333020 100644 --- a/packages/components/src/core/ComplexFilter/__storybook__/index.stories.tsx +++ b/packages/components/src/core/ComplexFilter/__storybook__/index.stories.tsx @@ -9,6 +9,11 @@ import { TestDemo } from "./stories/test"; export default { argTypes: { + isTriggerChangeOnOptionClick: { + control: { + type: "boolean", + }, + }, label: { control: { type: "text", @@ -44,6 +49,7 @@ export default { export const Default = { args: { + isTriggerChangeOnOptionClick: false, label: "Click Target", multiple: true, onChange: COMPLEX_FILTER_ON_CHANGE_OPTIONS[1], diff --git a/packages/components/src/core/ComplexFilter/__storybook__/stories/default.tsx b/packages/components/src/core/ComplexFilter/__storybook__/stories/default.tsx index 3b54298f5..879dd9e77 100644 --- a/packages/components/src/core/ComplexFilter/__storybook__/stories/default.tsx +++ b/packages/components/src/core/ComplexFilter/__storybook__/stories/default.tsx @@ -15,6 +15,7 @@ export const ComplexFilter = ( DropdownMenuProps={{ groupBy: (option: DefaultAutocompleteOption) => option.section as string, + width: 400, }} {...props} /> diff --git a/packages/components/src/core/ComplexFilter/components/Chips/index.tsx b/packages/components/src/core/ComplexFilter/components/Chips/index.tsx index 3de134535..313bf5a89 100644 --- a/packages/components/src/core/ComplexFilter/components/Chips/index.tsx +++ b/packages/components/src/core/ComplexFilter/components/Chips/index.tsx @@ -1,23 +1,40 @@ +import { AutocompleteValue } from "@mui/base"; import { DefaultAutocompleteOption } from "src/core/Autocomplete/components/AutocompleteBase"; import TagFilter from "src/core/TagFilter"; -interface Props { - value: DefaultAutocompleteOption | DefaultAutocompleteOption[] | null; +interface Props< + T extends DefaultAutocompleteOption, + Multiple extends boolean | undefined, + DisableClearable extends boolean | undefined, + FreeSolo extends boolean | undefined, +> { + value: AutocompleteValue; multiple?: boolean; onDelete: (option: DefaultAutocompleteOption) => void; } -const Chips = ({ +const Chips = < + T extends DefaultAutocompleteOption, + Multiple extends boolean | undefined, + DisableClearable extends boolean | undefined, + FreeSolo extends boolean | undefined, +>({ value, multiple = false, onDelete, -}: Props): JSX.Element | null => { +}: Props): JSX.Element | null => { if (!value) return null; if (!multiple) { const { name } = value as never; - return ; + return ( + onDelete(name)} + /> + ); } return ( @@ -26,7 +43,12 @@ const Chips = ({ const { name } = item; return ( - onDelete(item)} /> + onDelete(item)} + onClick={() => onDelete(item)} + /> ); })} diff --git a/packages/components/src/core/ComplexFilter/index.tsx b/packages/components/src/core/ComplexFilter/index.tsx index 5e8ea078a..653e816a8 100644 --- a/packages/components/src/core/ComplexFilter/index.tsx +++ b/packages/components/src/core/ComplexFilter/index.tsx @@ -36,6 +36,7 @@ export interface ComplexFilterProps< className?: string; PopperComponent?: typeof StyledPopper; InputDropdownComponent?: typeof InputDropdown; + isTriggerChangeOnOptionClick?: boolean; } // eslint-disable-next-line sonarjs/cognitive-complexity @@ -55,6 +56,7 @@ const ComplexFilter = < value: propValue, PopperComponent, InputDropdownComponent = InputDropdown, + isTriggerChangeOnOptionClick = false, ...rest }: ComplexFilterProps< T, @@ -67,16 +69,13 @@ const ComplexFilter = < const [open, setOpen] = useState(false); const [anchorEl, setAnchorEl] = useState(null); - const [value, setValue] = useState< - AutocompleteValue - >( - (multiple ? [] : null) as AutocompleteValue< - T, - Multiple, - DisableClearable, - FreeSolo - > - ); + const [value, setValue] = useState(getInitialValue()); + const [pendingValue, setPendingValue] = useState(getInitialValue()); + + useEffect(() => { + onChange(value); + setPendingValue(value); + }, [onChange, value]); useEffect(() => { if (isControlled) { @@ -98,13 +97,8 @@ const ComplexFilter = < /> - + value={value} multiple={multiple} onDelete={handleDelete} /> @@ -117,7 +111,7 @@ const ComplexFilter = < onClose={handleMenuSelectClose} search={search} multiple={multiple as Multiple} - value={value} + value={multiple ? pendingValue : value} onChange={handleChange} disableCloseOnSelect={multiple} options={options} @@ -132,6 +126,10 @@ const ComplexFilter = < function handleClick(event: React.MouseEvent) { if (open) { + if (multiple) { + setValue(pendingValue); + } + setOpen(false); if (anchorEl) { @@ -140,6 +138,10 @@ const ComplexFilter = < setAnchorEl(null); } else { + if (multiple) { + setPendingValue(value); + } + setAnchorEl(event.currentTarget); setOpen(true); } @@ -148,6 +150,10 @@ const ComplexFilter = < function handleClose() { if (open) { setOpen(false); + + if (multiple) { + setValue(pendingValue); + } } } @@ -163,14 +169,30 @@ const ComplexFilter = < } function handleChange( - event: React.ChangeEvent, + event: React.SyntheticEvent, + newValue: AutocompleteValue + ) { + if (multiple) { + if (isTriggerChangeOnOptionClick) { + setPendingValue(newValue); + + return setValueAndCallOnChange(event, newValue); + } + + return setPendingValue(newValue); + } + + setValueAndCallOnChange(event, newValue); + + if (!multiple) setOpen(false); + } + + function setValueAndCallOnChange( + event: React.SyntheticEvent, newValue: AutocompleteValue ) { setValue(newValue); onChange?.(newValue); - if (!multiple) { - setOpen(false); - } } function handleDelete(option: DefaultAutocompleteOption) { @@ -186,6 +208,22 @@ const ComplexFilter = < newValue as AutocompleteValue ); } + + function getInitialValue(): AutocompleteValue< + T, + Multiple, + DisableClearable, + FreeSolo + > { + return multiple + ? ([] as unknown as AutocompleteValue< + T, + Multiple, + DisableClearable, + FreeSolo + >) + : (null as AutocompleteValue); + } }; export default ComplexFilter; diff --git a/packages/components/src/core/DropdownMenu/index.tsx b/packages/components/src/core/DropdownMenu/index.tsx index bf80a206e..2d48cc176 100644 --- a/packages/components/src/core/DropdownMenu/index.tsx +++ b/packages/components/src/core/DropdownMenu/index.tsx @@ -126,6 +126,7 @@ const DropdownMenu = < const DefaultInputBaseProps = useMemo(() => { return { ...InputBaseProps, + autoFocus: true, onClick: noop, }; }, [InputBaseProps]); diff --git a/packages/components/src/core/Table/__tests__/__snapshots__/index.test.tsx.snap b/packages/components/src/core/Table/__tests__/__snapshots__/index.test.tsx.snap index ce5bbd613..8131066a1 100644 --- a/packages/components/src/core/Table/__tests__/__snapshots__/index.test.tsx.snap +++ b/packages/components/src/core/Table/__tests__/__snapshots__/index.test.tsx.snap @@ -123,7 +123,7 @@ exports[` Default story renders snapshot 1`] = ` />
Default story renders snapshot 1`] = ` class="cell-component css-4umyx6" >
Default story renders snapshot 1`] = `
Default story renders snapshot 1`] = `
Default story renders snapshot 1`] = `
Default story renders snapshot 1`] = `
Default story renders snapshot 1`] = ` />
Default story renders snapshot 1`] = ` />
Default story renders snapshot 1`] = `
{ .MuiSvgIcon-root { height: ${iconSizes?.xs.height}px; width: ${iconSizes?.xs.width}px; - padding-right: ${spaces?.xxs}px; - margin: 0 0 0 -${spaces?.xxxs}px; + margin: 0 ${spaces?.xxs}px 0 -${spaces?.xxxs}px; } .MuiChip-deleteIcon { @@ -89,8 +88,7 @@ const tagSizeL = (props: ExtraTagProps): SerializedStyles => { .MuiSvgIcon-root { height: ${iconSizes?.l.height}px; width: ${iconSizes?.l.width}px; - padding-right: ${spaces?.xxs}px; - margin: 0 0 0 -${spaces?.xxxs}px; + margin: 0 ${spaces?.xxs}px 0 -${spaces?.xxxs}px; } .MuiChip-deleteIcon { diff --git a/packages/components/src/core/TagFilter/__tests__/__snapshots__/index.test.tsx.snap b/packages/components/src/core/TagFilter/__tests__/__snapshots__/index.test.tsx.snap index 82852b2b8..35fb2ef38 100644 --- a/packages/components/src/core/TagFilter/__tests__/__snapshots__/index.test.tsx.snap +++ b/packages/components/src/core/TagFilter/__tests__/__snapshots__/index.test.tsx.snap @@ -16,7 +16,7 @@ exports[` Default story renders snapshot 1`] = `