Skip to content

Commit

Permalink
[Autocomplete] Accept external Listbox ref (#37325)
Browse files Browse the repository at this point in the history
Signed-off-by: Zeeshan Tamboli <[email protected]>
Co-authored-by: Zeeshan Tamboli <[email protected]>
  • Loading branch information
sai6855 and ZeeshanTamboli committed May 23, 2023
1 parent 6444a85 commit d5e8321
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 1 deletion.
1 change: 1 addition & 0 deletions packages/mui-material/src/Autocomplete/Autocomplete.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ export interface AutocompleteProps<
*/
ListboxProps?: ReturnType<ReturnType<typeof useAutocomplete>['getListboxProps']> & {
sx?: SxProps<Theme>;
ref?: React.Ref<Element>;
};
/**
* If `true`, the component is in a loading state.
Expand Down
8 changes: 7 additions & 1 deletion packages/mui-material/src/Autocomplete/Autocomplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import useThemeProps from '../styles/useThemeProps';
import styled from '../styles/styled';
import autocompleteClasses, { getAutocompleteUtilityClass } from './autocompleteClasses';
import capitalize from '../utils/capitalize';
import useForkRef from '../utils/useForkRef';

const useUtilityClasses = (ownerState) => {
const {
Expand Down Expand Up @@ -473,6 +474,10 @@ const Autocomplete = React.forwardRef(function Autocomplete(inProps, ref) {
const hasPopupIcon = (!freeSolo || forcePopupIcon === true) && forcePopupIcon !== false;

const { onMouseDown: handleInputMouseDown } = getInputProps();
const { ref: externalListboxRef } = ListboxProps ?? {};
const { ref: listboxRef, ...otherListboxProps } = getListboxProps();

const combinedListboxRef = useForkRef(listboxRef, externalListboxRef);

// If you modify this, make sure to keep the `AutocompleteOwnerState` type in sync.
const ownerState = {
Expand Down Expand Up @@ -666,8 +671,9 @@ const Autocomplete = React.forwardRef(function Autocomplete(inProps, ref) {
as={ListboxComponent}
className={classes.listbox}
ownerState={ownerState}
{...getListboxProps()}
{...otherListboxProps}
{...ListboxProps}
ref={combinedListboxRef}
>
{groupedOptions.map((option, index) => {
if (groupBy) {
Expand Down
11 changes: 11 additions & 0 deletions packages/mui-material/src/Autocomplete/Autocomplete.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,14 @@ function AutocompleteComponentsProps() {
/>
);
}

function CustomListboxRef() {
const ref = React.useRef(null);
return (
<Autocomplete
renderInput={(params) => <TextField {...params} />}
options={['one', 'two', 'three']}
ListboxProps={{ ref }}
/>
);
}
24 changes: 24 additions & 0 deletions packages/mui-material/src/Autocomplete/Autocomplete.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2502,6 +2502,30 @@ describe('<Autocomplete />', () => {
});
});

it('should not override internal listbox ref when external listbox ref is provided', () => {
const handleHighlightChange = spy();
const options = ['one', 'two', 'three'];
const ref = React.createRef(null);
render(
<Autocomplete
defaultValue={options[0]}
onHighlightChange={handleHighlightChange}
options={options}
ListboxProps={{ ref }}
open
renderInput={(params) => <TextField {...params} autoFocus />}
/>,
);
expect(handleHighlightChange.callCount).to.equal(
// FIXME: highlighted index implementation should be implemented using React not the DOM.
React.version.startsWith('18') ? 2 : 1,
);
expect(handleHighlightChange.args[0]).to.deep.equal([undefined, options[0], 'auto']);
if (React.version.startsWith('18')) {
expect(handleHighlightChange.args[1]).to.deep.equal([undefined, options[0], 'auto']);
}
});

describe('prop: onHighlightChange', () => {
it('should trigger event when default value is passed', () => {
const handleHighlightChange = spy();
Expand Down

0 comments on commit d5e8321

Please sign in to comment.