diff --git a/docs/data/material/components/autocomplete/GitHubLabel.js b/docs/data/material/components/autocomplete/GitHubLabel.js
index 22396f95ee43b1..404687f7112a04 100644
--- a/docs/data/material/components/autocomplete/GitHubLabel.js
+++ b/docs/data/material/components/autocomplete/GitHubLabel.js
@@ -192,7 +192,6 @@ export default function GitHubLabel() {
setPendingValue(newValue);
}}
disableCloseOnSelect
- PopperComponent={PopperComponent}
renderTags={() => null}
noOptionsText="No labels"
renderOption={(props, option, { selected }) => (
@@ -255,6 +254,9 @@ export default function GitHubLabel() {
placeholder="Filter labels"
/>
)}
+ slots={{
+ popper: PopperComponent,
+ }}
/>
diff --git a/docs/data/material/components/autocomplete/GitHubLabel.tsx b/docs/data/material/components/autocomplete/GitHubLabel.tsx
index 1b93212d7a0cb8..ec4c222d4abc71 100644
--- a/docs/data/material/components/autocomplete/GitHubLabel.tsx
+++ b/docs/data/material/components/autocomplete/GitHubLabel.tsx
@@ -198,7 +198,6 @@ export default function GitHubLabel() {
setPendingValue(newValue);
}}
disableCloseOnSelect
- PopperComponent={PopperComponent}
renderTags={() => null}
noOptionsText="No labels"
renderOption={(props, option, { selected }) => (
@@ -261,6 +260,9 @@ export default function GitHubLabel() {
placeholder="Filter labels"
/>
)}
+ slots={{
+ popper: PopperComponent,
+ }}
/>
diff --git a/docs/data/material/components/autocomplete/Virtualize.js b/docs/data/material/components/autocomplete/Virtualize.js
index 7b62b6c4f7b938..e5c812f9e366cc 100644
--- a/docs/data/material/components/autocomplete/Virtualize.js
+++ b/docs/data/material/components/autocomplete/Virtualize.js
@@ -141,13 +141,15 @@ export default function Virtualize() {
id="virtualize-demo"
sx={{ width: 300 }}
disableListWrap
- PopperComponent={StyledPopper}
- ListboxComponent={ListboxComponent}
options={OPTIONS}
groupBy={(option) => option[0].toUpperCase()}
renderInput={(params) => }
renderOption={(props, option, state) => [props, option, state.index]}
renderGroup={(params) => params}
+ slots={{
+ popper: StyledPopper,
+ listbox: ListboxComponent,
+ }}
/>
);
}
diff --git a/docs/data/material/components/autocomplete/Virtualize.tsx b/docs/data/material/components/autocomplete/Virtualize.tsx
index cd6eee98854ff5..3e61456f3f6339 100644
--- a/docs/data/material/components/autocomplete/Virtualize.tsx
+++ b/docs/data/material/components/autocomplete/Virtualize.tsx
@@ -141,8 +141,6 @@ export default function Virtualize() {
id="virtualize-demo"
sx={{ width: 300 }}
disableListWrap
- PopperComponent={StyledPopper}
- ListboxComponent={ListboxComponent}
options={OPTIONS}
groupBy={(option) => option[0].toUpperCase()}
renderInput={(params) => }
@@ -150,6 +148,10 @@ export default function Virtualize() {
[props, option, state.index] as React.ReactNode
}
renderGroup={(params) => params as any}
+ slots={{
+ popper: StyledPopper,
+ listbox: ListboxComponent,
+ }}
/>
);
}
diff --git a/docs/data/material/components/autocomplete/Virtualize.tsx.preview b/docs/data/material/components/autocomplete/Virtualize.tsx.preview
index 7aa80fd1fd266e..d243091ed77894 100644
--- a/docs/data/material/components/autocomplete/Virtualize.tsx.preview
+++ b/docs/data/material/components/autocomplete/Virtualize.tsx.preview
@@ -2,8 +2,6 @@
id="virtualize-demo"
sx={{ width: 300 }}
disableListWrap
- PopperComponent={StyledPopper}
- ListboxComponent={ListboxComponent}
options={OPTIONS}
groupBy={(option) => option[0].toUpperCase()}
renderInput={(params) => }
@@ -11,4 +9,8 @@
[props, option, state.index] as React.ReactNode
}
renderGroup={(params) => params as any}
+ slots={{
+ popper: StyledPopper,
+ listbox: ListboxComponent,
+ }}
/>
\ No newline at end of file
diff --git a/docs/data/material/migration/migrating-from-deprecated-apis/migrating-from-deprecated-apis.md b/docs/data/material/migration/migrating-from-deprecated-apis/migrating-from-deprecated-apis.md
index 43ac60f957e2e2..af17a0b9f66f9a 100644
--- a/docs/data/material/migration/migrating-from-deprecated-apis/migrating-from-deprecated-apis.md
+++ b/docs/data/material/migration/migrating-from-deprecated-apis/migrating-from-deprecated-apis.md
@@ -227,6 +227,46 @@ Here's how to migrate:
},
```
+## Autocomplete
+
+Use the [codemod](https://github.com/mui/material-ui/tree/HEAD/packages/mui-codemod#autocomplete-props) below to migrate the code as described in the following sections:
+
+```bash
+npx @mui/codemod@next deprecations/autocomplete-props
+```
+
+### \*Component props
+
+All of the Autocomplete's slot (`*Component`) props were deprecated in favor of equivalent `slots` entries:
+
+```diff
+
+```
+
+### \*Props props
+
+All of the Autocomplete's slot props (`*Props`) props were deprecated in favor of equivalent `slotProps` entries:
+
+```diff
+
+```
+
## Avatar
Use the [codemod](https://github.com/mui/material-ui/tree/HEAD/packages/mui-codemod#avatar-props) below to migrate the code as described in the following sections:
diff --git a/docs/pages/material-ui/api/autocomplete.json b/docs/pages/material-ui/api/autocomplete.json
index e0e4d60b0a7e5a..fcb754234fd1ba 100644
--- a/docs/pages/material-ui/api/autocomplete.json
+++ b/docs/pages/material-ui/api/autocomplete.json
@@ -16,7 +16,11 @@
},
"default": "false"
},
- "ChipProps": { "type": { "name": "object" } },
+ "ChipProps": {
+ "type": { "name": "object" },
+ "deprecated": true,
+ "deprecationInfo": "Use slotProps.chip
instead. This prop will be removed in v7. See Migrating from deprecated APIs for more details."
+ },
"classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } },
"clearIcon": { "type": { "name": "node" }, "default": "" },
"clearOnBlur": { "type": { "name": "bool" }, "default": "!props.freeSolo" },
@@ -92,8 +96,17 @@
}
},
"limitTags": { "type": { "name": "custom", "description": "integer" }, "default": "-1" },
- "ListboxComponent": { "type": { "name": "elementType" }, "default": "'ul'" },
- "ListboxProps": { "type": { "name": "object" } },
+ "ListboxComponent": {
+ "type": { "name": "elementType" },
+ "default": "'ul'",
+ "deprecated": true,
+ "deprecationInfo": "Use slots.listbox
instead. This prop will be removed in v7. See Migrating from deprecated APIs for more details."
+ },
+ "ListboxProps": {
+ "type": { "name": "object" },
+ "deprecated": true,
+ "deprecationInfo": "Use slotProps.listbox
instead. This prop will be removed in v7. See Migrating from deprecated APIs for more details."
+ },
"loading": { "type": { "name": "bool" }, "default": "false" },
"loadingText": { "type": { "name": "node" }, "default": "'Loading…'" },
"multiple": { "type": { "name": "bool" }, "default": "false" },
@@ -136,8 +149,18 @@
"open": { "type": { "name": "bool" } },
"openOnFocus": { "type": { "name": "bool" }, "default": "false" },
"openText": { "type": { "name": "string" }, "default": "'Open'" },
- "PaperComponent": { "type": { "name": "elementType" }, "default": "Paper" },
- "PopperComponent": { "type": { "name": "elementType" }, "default": "Popper" },
+ "PaperComponent": {
+ "type": { "name": "elementType" },
+ "default": "Paper",
+ "deprecated": true,
+ "deprecationInfo": "Use slots.paper
instead. This prop will be removed in v7. See Migrating from deprecated APIs for more details."
+ },
+ "PopperComponent": {
+ "type": { "name": "elementType" },
+ "default": "Popper",
+ "deprecated": true,
+ "deprecationInfo": "Use slots.popper
instead. This prop will be removed in v7. See Migrating from deprecated APIs for more details."
+ },
"popupIcon": { "type": { "name": "node" }, "default": "" },
"readOnly": { "type": { "name": "bool" }, "default": "false" },
"renderGroup": {
@@ -172,7 +195,14 @@
"slotProps": {
"type": {
"name": "shape",
- "description": "{ clearIndicator?: object, paper?: object, popper?: object, popupIndicator?: object }"
+ "description": "{ chip?: func
| object, clearIndicator?: func
| object, listbox?: func
| object, paper?: func
| object, popper?: func
| object, popupIndicator?: func
| object }"
+ },
+ "default": "{}"
+ },
+ "slots": {
+ "type": {
+ "name": "shape",
+ "description": "{ listbox?: elementType, paper?: elementType, popper?: elementType }"
},
"default": "{}"
},
@@ -190,6 +220,26 @@
"import Autocomplete from '@mui/material/Autocomplete';",
"import { Autocomplete } from '@mui/material';"
],
+ "slots": [
+ {
+ "name": "listbox",
+ "description": "The component used to render the listbox.",
+ "default": "'ul'",
+ "class": "MuiAutocomplete-listbox"
+ },
+ {
+ "name": "paper",
+ "description": "The component used to render the body of the popup.",
+ "default": "Paper",
+ "class": "MuiAutocomplete-paper"
+ },
+ {
+ "name": "popper",
+ "description": "The component used to position the popup.",
+ "default": "Popper",
+ "class": "MuiAutocomplete-popper"
+ }
+ ],
"classes": [
{
"key": "clearIndicator",
@@ -269,12 +319,6 @@
"description": "Styles applied to the Input element.",
"isGlobal": false
},
- {
- "key": "listbox",
- "className": "MuiAutocomplete-listbox",
- "description": "Styles applied to the listbox component.",
- "isGlobal": false
- },
{
"key": "loading",
"className": "MuiAutocomplete-loading",
@@ -293,18 +337,6 @@
"description": "Styles applied to the option elements.",
"isGlobal": false
},
- {
- "key": "paper",
- "className": "MuiAutocomplete-paper",
- "description": "Styles applied to the Paper component.",
- "isGlobal": false
- },
- {
- "key": "popper",
- "className": "MuiAutocomplete-popper",
- "description": "Styles applied to the popper element.",
- "isGlobal": false
- },
{
"key": "popperDisablePortal",
"className": "MuiAutocomplete-popperDisablePortal",
@@ -351,7 +383,6 @@
"spread": true,
"themeDefaultProps": true,
"muiName": "MuiAutocomplete",
- "forwardsRefTo": "HTMLDivElement",
"filename": "/packages/mui-material/src/Autocomplete/Autocomplete.js",
"inheritance": null,
"demos": "",
diff --git a/docs/translations/api-docs/autocomplete/autocomplete.json b/docs/translations/api-docs/autocomplete/autocomplete.json
index b257a40f8d3e01..9eaf631c6855cc 100644
--- a/docs/translations/api-docs/autocomplete/autocomplete.json
+++ b/docs/translations/api-docs/autocomplete/autocomplete.json
@@ -189,6 +189,7 @@
},
"size": { "description": "The size of the component." },
"slotProps": { "description": "The props used for each slot inside." },
+ "slots": { "description": "The components used for each slot inside." },
"sx": {
"description": "The system prop that allows defining system overrides as well as additional CSS styles."
},
@@ -245,10 +246,6 @@
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the Input element"
},
- "listbox": {
- "description": "Styles applied to {{nodeName}}.",
- "nodeName": "the listbox component"
- },
"loading": {
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the loading wrapper"
@@ -261,14 +258,6 @@
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the option elements"
},
- "paper": {
- "description": "Styles applied to {{nodeName}}.",
- "nodeName": "the Paper component"
- },
- "popper": {
- "description": "Styles applied to {{nodeName}}.",
- "nodeName": "the popper element"
- },
"popperDisablePortal": {
"description": "Styles applied to {{nodeName}} if {{conditions}}.",
"nodeName": "the popper element",
@@ -299,5 +288,10 @@
"nodeName": "the tag elements",
"conditions": "for example the chips if size=\"small\"
"
}
+ },
+ "slotDescriptions": {
+ "listbox": "The component used to render the listbox.",
+ "paper": "The component used to render the body of the popup.",
+ "popper": "The component used to position the popup."
}
}
diff --git a/packages/mui-codemod/README.md b/packages/mui-codemod/README.md
index 604a054a6e8ac0..0f504c865902a1 100644
--- a/packages/mui-codemod/README.md
+++ b/packages/mui-codemod/README.md
@@ -259,6 +259,52 @@ npx @mui/codemod@next deprecations/alert-classes
npx @mui/codemod@next deprecations/alert-props
```
+#### `autocomplete-props`
+
+```diff
+
+```
+
+```diff
+ MuiAutocomplete: {
+ defaultProps: {
+- ChipProps: { height: 10 },
+- PaperComponent: CustomPaper,
+- PopperComponent: CustomPopper,
+- ListboxComponent: CustomListbox,
+- ListboxProps: { height: 12 },
++ slots: {
++ paper: CustomPaper,
++ popper: CustomPopper,
++ listbox: CustomListbox,
++ },
++ slotProps: {
++ chip: { height: 10 },
++ listbox: { height: 12 },
++ },
+ },
+ },
+```
+
+```bash
+npx @mui/codemod@next deprecations/autocomplete-props
+```
+
#### `avatar-props`
```diff
diff --git a/packages/mui-codemod/src/deprecations/all/deprecations-all.js b/packages/mui-codemod/src/deprecations/all/deprecations-all.js
index 3bddd0595b93e3..5fcc872a52ac17 100644
--- a/packages/mui-codemod/src/deprecations/all/deprecations-all.js
+++ b/packages/mui-codemod/src/deprecations/all/deprecations-all.js
@@ -1,4 +1,5 @@
import transformAccordionProps from '../accordion-props';
+import transformAutocompleteProps from '../autocomplete-props';
import transformFormControlLabelProps from '../form-control-label-props';
import transformAvatarProps from '../avatar-props';
import transformDividerProps from '../divider-props';
@@ -19,6 +20,7 @@ import transformStepConnectorClasses from '../step-connector-classes';
*/
export default function deprecationsAll(file, api, options) {
file.source = transformAccordionProps(file, api, options);
+ file.source = transformAutocompleteProps(file, api, options);
file.source = transformFormControlLabelProps(file, api, options);
file.source = transformAvatarProps(file, api, options);
file.source = transformDividerProps(file, api, options);
diff --git a/packages/mui-codemod/src/deprecations/autocomplete-props/autocomplete-props.js b/packages/mui-codemod/src/deprecations/autocomplete-props/autocomplete-props.js
new file mode 100644
index 00000000000000..3b5204f0bd642d
--- /dev/null
+++ b/packages/mui-codemod/src/deprecations/autocomplete-props/autocomplete-props.js
@@ -0,0 +1,49 @@
+import movePropIntoSlots from '../utils/movePropIntoSlots';
+import movePropIntoSlotProps from '../utils/movePropIntoSlotProps';
+
+/**
+ * @param {import('jscodeshift').FileInfo} file
+ * @param {import('jscodeshift').API} api
+ */
+export default function transformer(file, api, options) {
+ const j = api.jscodeshift;
+ const root = j(file.source);
+ const printOptions = options.printOptions;
+
+ movePropIntoSlots(j, {
+ root,
+ componentName: 'Autocomplete',
+ propName: 'PaperComponent',
+ slotName: 'paper',
+ });
+
+ movePropIntoSlots(j, {
+ root,
+ componentName: 'Autocomplete',
+ propName: 'PopperComponent',
+ slotName: 'popper',
+ });
+
+ movePropIntoSlots(j, {
+ root,
+ componentName: 'Autocomplete',
+ propName: 'ListboxComponent',
+ slotName: 'listbox',
+ });
+
+ movePropIntoSlotProps(j, {
+ root,
+ componentName: 'Autocomplete',
+ propName: 'ListboxProps',
+ slotName: 'listbox',
+ });
+
+ movePropIntoSlotProps(j, {
+ root,
+ componentName: 'Autocomplete',
+ propName: 'ChipProps',
+ slotName: 'chip',
+ });
+
+ return root.toSource(printOptions);
+}
diff --git a/packages/mui-codemod/src/deprecations/autocomplete-props/autocomplete-props.test.js b/packages/mui-codemod/src/deprecations/autocomplete-props/autocomplete-props.test.js
new file mode 100644
index 00000000000000..e5909cad32dab3
--- /dev/null
+++ b/packages/mui-codemod/src/deprecations/autocomplete-props/autocomplete-props.test.js
@@ -0,0 +1,53 @@
+import path from 'path';
+import { expect } from 'chai';
+import { jscodeshift } from '../../../testUtils';
+import transform from './autocomplete-props';
+import readFile from '../../util/readFile';
+
+function read(fileName) {
+ return readFile(path.join(__dirname, fileName));
+}
+
+describe('@mui/codemod', () => {
+ describe('deprecations', () => {
+ describe('autocomplete-props', () => {
+ it('transforms props as needed', () => {
+ const actual = transform({ source: read('./test-cases/actual.js') }, { jscodeshift }, {});
+
+ const expected = read('./test-cases/expected.js');
+ expect(actual).to.equal(expected, 'The transformed version should be correct');
+ });
+
+ it('should be idempotent', () => {
+ const actual = transform({ source: read('./test-cases/expected.js') }, { jscodeshift }, {});
+
+ const expected = read('./test-cases/expected.js');
+ expect(actual).to.equal(expected, 'The transformed version should be correct');
+ });
+ });
+
+ describe('[theme] autocomplete-props', () => {
+ it('transforms props as needed', () => {
+ const actual = transform(
+ { source: read('./test-cases/theme.actual.js') },
+ { jscodeshift },
+ {},
+ );
+
+ const expected = read('./test-cases/theme.expected.js');
+ expect(actual).to.equal(expected, 'The transformed version should be correct');
+ });
+
+ it('should be idempotent', () => {
+ const actual = transform(
+ { source: read('./test-cases/theme.expected.js') },
+ { jscodeshift },
+ {},
+ );
+
+ const expected = read('./test-cases/theme.expected.js');
+ expect(actual).to.equal(expected, 'The transformed version should be correct');
+ });
+ });
+ });
+});
diff --git a/packages/mui-codemod/src/deprecations/autocomplete-props/index.js b/packages/mui-codemod/src/deprecations/autocomplete-props/index.js
new file mode 100644
index 00000000000000..b2b9a74aa9c035
--- /dev/null
+++ b/packages/mui-codemod/src/deprecations/autocomplete-props/index.js
@@ -0,0 +1 @@
+export { default } from './autocomplete-props';
diff --git a/packages/mui-codemod/src/deprecations/autocomplete-props/test-cases/actual.js b/packages/mui-codemod/src/deprecations/autocomplete-props/test-cases/actual.js
new file mode 100644
index 00000000000000..8003251b598cf3
--- /dev/null
+++ b/packages/mui-codemod/src/deprecations/autocomplete-props/test-cases/actual.js
@@ -0,0 +1,37 @@
+import Autocomplete from '@mui/material/Autocomplete';
+import {Autocomplete as MyAutocomplete} from '@mui/material';
+
+;
+
+;
+
+;
+
+
diff --git a/packages/mui-codemod/src/deprecations/autocomplete-props/test-cases/expected.js b/packages/mui-codemod/src/deprecations/autocomplete-props/test-cases/expected.js
new file mode 100644
index 00000000000000..f816b74b646c81
--- /dev/null
+++ b/packages/mui-codemod/src/deprecations/autocomplete-props/test-cases/expected.js
@@ -0,0 +1,44 @@
+import Autocomplete from '@mui/material/Autocomplete';
+import {Autocomplete as MyAutocomplete} from '@mui/material';
+
+;
+
+;
+
+;
+
+
diff --git a/packages/mui-codemod/src/deprecations/autocomplete-props/test-cases/theme.actual.js b/packages/mui-codemod/src/deprecations/autocomplete-props/test-cases/theme.actual.js
new file mode 100644
index 00000000000000..0bc3ca5dc4d4bf
--- /dev/null
+++ b/packages/mui-codemod/src/deprecations/autocomplete-props/test-cases/theme.actual.js
@@ -0,0 +1,26 @@
+fn({
+ MuiAutocomplete: {
+ defaultProps: {
+ ChipProps: { height: 10 },
+ PaperComponent: CustomPaper,
+ PopperComponent: CustomPopper,
+ ListboxComponent: CustomListbox,
+ ListboxProps: { height: 12 },
+ },
+ },
+});
+
+fn({
+ MuiAutocomplete: {
+ defaultProps: {
+ ChipProps: { height: 10 },
+ PaperComponent: CustomPaper,
+ PopperComponent: CustomPopper,
+ ListboxComponent: CustomListbox,
+ ListboxProps: { height: 12 },
+ slotProps: {
+ popupIndicator: { width: 20 }
+ },
+ },
+ },
+});
diff --git a/packages/mui-codemod/src/deprecations/autocomplete-props/test-cases/theme.expected.js b/packages/mui-codemod/src/deprecations/autocomplete-props/test-cases/theme.expected.js
new file mode 100644
index 00000000000000..bc7b33f0055781
--- /dev/null
+++ b/packages/mui-codemod/src/deprecations/autocomplete-props/test-cases/theme.expected.js
@@ -0,0 +1,34 @@
+fn({
+ MuiAutocomplete: {
+ defaultProps: {
+ slots: {
+ paper: CustomPaper,
+ popper: CustomPopper,
+ listbox: CustomListbox
+ },
+
+ slotProps: {
+ listbox: { height: 12 },
+ chip: { height: 10 }
+ }
+ },
+ },
+});
+
+fn({
+ MuiAutocomplete: {
+ defaultProps: {
+ slotProps: {
+ popupIndicator: { width: 20 },
+ listbox: { height: 12 },
+ chip: { height: 10 }
+ },
+
+ slots: {
+ paper: CustomPaper,
+ popper: CustomPopper,
+ listbox: CustomListbox
+ }
+ },
+ },
+});
diff --git a/packages/mui-material/src/Autocomplete/Autocomplete.d.ts b/packages/mui-material/src/Autocomplete/Autocomplete.d.ts
index bc93447fab6e13..70772d58b3756e 100644
--- a/packages/mui-material/src/Autocomplete/Autocomplete.d.ts
+++ b/packages/mui-material/src/Autocomplete/Autocomplete.d.ts
@@ -17,6 +17,7 @@ import { ChipProps, ChipTypeMap } from '@mui/material/Chip';
import { PaperProps } from '@mui/material/Paper';
import { PopperProps } from '@mui/material/Popper';
import { AutocompleteClasses } from './autocompleteClasses';
+import { CreateSlotsAndSlotProps, SlotProps } from '../utils/types';
export {
AutocompleteChangeDetails,
@@ -84,6 +85,75 @@ export interface AutocompleteRenderInputParams {
export interface AutocompletePropsSizeOverrides {}
+export interface AutocompleteSlots {
+ /**
+ * The component used to render the listbox.
+ * @default 'ul'
+ */
+ listbox?: React.JSXElementConstructor>;
+ /**
+ * The component used to render the body of the popup.
+ * @default Paper
+ */
+ paper?: React.JSXElementConstructor;
+ /**
+ * The component used to position the popup.
+ * @default Popper
+ */
+ popper?: React.JSXElementConstructor;
+}
+
+export type AutocompleteSlotsAndSlotProps<
+ Value,
+ Multiple extends boolean | undefined,
+ DisableClearable extends boolean | undefined,
+ FreeSolo extends boolean | undefined,
+ ChipComponent extends React.ElementType = ChipTypeMap['defaultComponent'],
+> = CreateSlotsAndSlotProps<
+ AutocompleteSlots,
+ {
+ chip: SlotProps<
+ React.ElementType>>,
+ {},
+ AutocompleteOwnerState
+ >;
+ clearIndicator: SlotProps<
+ React.ElementType>,
+ {},
+ AutocompleteOwnerState
+ >;
+ /**
+ * Props applied to the Listbox element.
+ */
+ listbox: SlotProps<
+ React.ElementType<
+ ReturnType['getListboxProps']> & {
+ sx?: SxProps;
+ ref?: React.Ref;
+ }
+ >,
+ {},
+ AutocompleteOwnerState
+ >;
+
+ paper: SlotProps<
+ React.ElementType>,
+ {},
+ AutocompleteOwnerState
+ >;
+ popper: SlotProps<
+ React.ElementType>,
+ {},
+ AutocompleteOwnerState
+ >;
+ popupIndicator: SlotProps<
+ React.ElementType>,
+ {},
+ AutocompleteOwnerState
+ >;
+ }
+>;
+
export interface AutocompleteProps<
Value,
Multiple extends boolean | undefined,
@@ -91,9 +161,11 @@ export interface AutocompleteProps<
FreeSolo extends boolean | undefined,
ChipComponent extends React.ElementType = ChipTypeMap['defaultComponent'],
> extends UseAutocompleteProps,
- StandardProps, 'defaultValue' | 'onChange' | 'children'> {
+ StandardProps, 'defaultValue' | 'onChange' | 'children'>,
+ AutocompleteSlotsAndSlotProps {
/**
* Props applied to the [`Chip`](/material-ui/api/chip/) element.
+ * @deprecated Use `slotProps.chip` instead. This prop will be removed in v7. See [Migrating from deprecated APIs](/material-ui/migration/migrating-from-deprecated-apis/) for more details.
*/
ChipProps?: ChipProps;
/**
@@ -160,10 +232,12 @@ export interface AutocompleteProps<
/**
* The component used to render the listbox.
* @default 'ul'
+ * @deprecated Use `slots.listbox` instead. This prop will be removed in v7. See [Migrating from deprecated APIs](/material-ui/migration/migrating-from-deprecated-apis/) for more details.
*/
ListboxComponent?: React.JSXElementConstructor>;
/**
* Props applied to the Listbox element.
+ * @deprecated Use `slotProps.listbox` instead. This prop will be removed in v7. See [Migrating from deprecated APIs](/material-ui/migration/migrating-from-deprecated-apis/) for more details.
*/
ListboxProps?: ReturnType['getListboxProps']> & {
sx?: SxProps;
@@ -208,11 +282,13 @@ export interface AutocompleteProps<
/**
* The component used to render the body of the popup.
* @default Paper
+ * @deprecated Use `slots.paper` instead. This prop will be removed in v7. See [Migrating from deprecated APIs](/material-ui/migration/migrating-from-deprecated-apis/) for more details.
*/
PaperComponent?: React.JSXElementConstructor>;
/**
* The component used to position the popup.
* @default Popper
+ * @deprecated Use `slots.popper` instead. This prop will be removed in v7. See [Migrating from deprecated APIs](/material-ui/migration/migrating-from-deprecated-apis/) for more details.
*/
PopperComponent?: React.JSXElementConstructor;
/**
@@ -272,16 +348,6 @@ export interface AutocompleteProps<
* @default 'medium'
*/
size?: OverridableStringUnion<'small' | 'medium', AutocompletePropsSizeOverrides>;
- /**
- * The props used for each slot inside.
- * @default {}
- */
- slotProps?: {
- clearIndicator?: Partial;
- paper?: PaperProps;
- popper?: Partial;
- popupIndicator?: Partial;
- };
/**
* The system prop that allows defining system overrides as well as additional CSS styles.
*/
diff --git a/packages/mui-material/src/Autocomplete/Autocomplete.js b/packages/mui-material/src/Autocomplete/Autocomplete.js
index 2aea5bf347fb6b..f3e1b3e4846606 100644
--- a/packages/mui-material/src/Autocomplete/Autocomplete.js
+++ b/packages/mui-material/src/Autocomplete/Autocomplete.js
@@ -21,7 +21,7 @@ import ArrowDropDownIcon from '../internal/svg-icons/ArrowDropDown';
import { styled, createUseThemeProps } from '../zero-styled';
import autocompleteClasses, { getAutocompleteUtilityClass } from './autocompleteClasses';
import capitalize from '../utils/capitalize';
-import useForkRef from '../utils/useForkRef';
+import useSlot from '../utils/useSlot';
const useThemeProps = createUseThemeProps('MuiAutocomplete');
@@ -412,7 +412,7 @@ const Autocomplete = React.forwardRef(function Autocomplete(inProps, ref) {
autoHighlight = false,
autoSelect = false,
blurOnSelect = false,
- ChipProps,
+ ChipProps: ChipPropsProp,
className,
clearIcon = ,
clearOnBlur = !props.freeSolo,
@@ -443,8 +443,8 @@ const Autocomplete = React.forwardRef(function Autocomplete(inProps, ref) {
includeInputInList = false,
inputValue: inputValueProp,
limitTags = -1,
- ListboxComponent = 'ul',
- ListboxProps,
+ ListboxComponent: ListboxComponentProp,
+ ListboxProps: ListboxPropsProp,
loading = false,
loadingText = 'Loading…',
multiple = false,
@@ -458,8 +458,8 @@ const Autocomplete = React.forwardRef(function Autocomplete(inProps, ref) {
openOnFocus = false,
openText = 'Open',
options,
- PaperComponent = Paper,
- PopperComponent = Popper,
+ PaperComponent: PaperComponentProp,
+ PopperComponent: PopperComponentProp,
popupIcon = ,
readOnly = false,
renderGroup: renderGroupProp,
@@ -468,6 +468,7 @@ const Autocomplete = React.forwardRef(function Autocomplete(inProps, ref) {
renderTags,
selectOnFocus = !props.freeSolo,
size = 'medium',
+ slots = {},
slotProps = {},
value: valueProp,
...other
@@ -500,11 +501,8 @@ 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);
-
const defaultGetOptionLabel = (option) => option.label ?? option;
const getOptionLabel = getOptionLabelProp || defaultGetOptionLabel;
@@ -525,6 +523,51 @@ const Autocomplete = React.forwardRef(function Autocomplete(inProps, ref) {
const classes = useUtilityClasses(ownerState);
+ const externalForwardedProps = {
+ slots: {
+ listbox: ListboxComponentProp,
+ paper: PaperComponentProp,
+ popper: PopperComponentProp,
+ ...slots,
+ },
+ slotProps: {
+ chip: ChipPropsProp,
+ listbox: ListboxPropsProp,
+ ...componentsProps,
+ ...slotProps,
+ },
+ };
+
+ const [ListboxSlot, listboxProps] = useSlot('listbox', {
+ elementType: 'ul',
+ externalForwardedProps,
+ ownerState,
+ className: classes.listbox,
+ additionalProps: otherListboxProps,
+ ref: listboxRef,
+ });
+
+ const [PaperSlot, paperProps] = useSlot('paper', {
+ elementType: Paper,
+ externalForwardedProps,
+ ownerState,
+ className: classes.paper,
+ });
+
+ const [PopperSlot, popperProps] = useSlot('popper', {
+ elementType: Popper,
+ externalForwardedProps,
+ ownerState,
+ className: classes.popper,
+ additionalProps: {
+ disablePortal,
+ style: { width: anchorEl ? anchorEl.clientWidth : null },
+ role: 'presentation',
+ anchorEl,
+ open: popupOpen,
+ },
+ });
+
let startAdornment;
if (multiple && value.length > 0) {
@@ -542,7 +585,7 @@ const Autocomplete = React.forwardRef(function Autocomplete(inProps, ref) {
label={getOptionLabel(option)}
size={size}
{...getCustomizedTagProps({ index })}
- {...ChipProps}
+ {...externalForwardedProps.slotProps.chip}
/>
));
}
@@ -601,29 +644,12 @@ const Autocomplete = React.forwardRef(function Autocomplete(inProps, ref) {
);
};
- const clearIndicatorSlotProps = slotProps.clearIndicator ?? componentsProps.clearIndicator;
- const paperSlotProps = slotProps.paper ?? componentsProps.paper;
- const popperSlotProps = slotProps.popper ?? componentsProps.popper;
- const popupIndicatorSlotProps = slotProps.popupIndicator ?? componentsProps.popupIndicator;
+ const clearIndicatorSlotProps = externalForwardedProps.slotProps.clearIndicator;
+ const popupIndicatorSlotProps = externalForwardedProps.slotProps.popupIndicator;
const renderAutocompletePopperChildren = (children) => (
-
-
+
+
{children}
@@ -632,14 +658,7 @@ const Autocomplete = React.forwardRef(function Autocomplete(inProps, ref) {
let autocompletePopper = null;
if (groupedOptions.length > 0) {
autocompletePopper = renderAutocompletePopperChildren(
-
+
{groupedOptions.map((option, index) => {
if (groupBy) {
return renderGroup({
@@ -784,6 +803,7 @@ Autocomplete.propTypes /* remove-proptypes */ = {
blurOnSelect: PropTypes.oneOfType([PropTypes.oneOf(['mouse', 'touch']), PropTypes.bool]),
/**
* Props applied to the [`Chip`](/material-ui/api/chip/) element.
+ * @deprecated Use `slotProps.chip` instead. This prop will be removed in v7. See [Migrating from deprecated APIs](/material-ui/migration/migrating-from-deprecated-apis/) for more details.
*/
ChipProps: PropTypes.object,
/**
@@ -991,10 +1011,12 @@ Autocomplete.propTypes /* remove-proptypes */ = {
/**
* The component used to render the listbox.
* @default 'ul'
+ * @deprecated Use `slots.listbox` instead. This prop will be removed in v7. See [Migrating from deprecated APIs](/material-ui/migration/migrating-from-deprecated-apis/) for more details.
*/
ListboxComponent: PropTypes.elementType,
/**
* Props applied to the Listbox element.
+ * @deprecated Use `slotProps.listbox` instead. This prop will be removed in v7. See [Migrating from deprecated APIs](/material-ui/migration/migrating-from-deprecated-apis/) for more details.
*/
ListboxProps: PropTypes.object,
/**
@@ -1089,11 +1111,13 @@ Autocomplete.propTypes /* remove-proptypes */ = {
/**
* The component used to render the body of the popup.
* @default Paper
+ * @deprecated Use `slots.paper` instead. This prop will be removed in v7. See [Migrating from deprecated APIs](/material-ui/migration/migrating-from-deprecated-apis/) for more details.
*/
PaperComponent: PropTypes.elementType,
/**
* The component used to position the popup.
* @default Popper
+ * @deprecated Use `slots.popper` instead. This prop will be removed in v7. See [Migrating from deprecated APIs](/material-ui/migration/migrating-from-deprecated-apis/) for more details.
*/
PopperComponent: PropTypes.elementType,
/**
@@ -1157,11 +1181,22 @@ Autocomplete.propTypes /* remove-proptypes */ = {
* The props used for each slot inside.
* @default {}
*/
- slotProps: PropTypes.shape({
- clearIndicator: PropTypes.object,
- paper: PropTypes.object,
- popper: PropTypes.object,
- popupIndicator: PropTypes.object,
+ slotProps: PropTypes /* @typescript-to-proptypes-ignore */.shape({
+ chip: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
+ clearIndicator: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
+ listbox: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
+ paper: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
+ popper: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
+ popupIndicator: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
+ }),
+ /**
+ * The components used for each slot inside.
+ * @default {}
+ */
+ slots: PropTypes.shape({
+ listbox: PropTypes.elementType,
+ paper: PropTypes.elementType,
+ popper: PropTypes.elementType,
}),
/**
* The system prop that allows defining system overrides as well as additional CSS styles.
diff --git a/packages/mui-material/src/Autocomplete/Autocomplete.test.js b/packages/mui-material/src/Autocomplete/Autocomplete.test.js
index f6a04adaebb858..26852846f74ddd 100644
--- a/packages/mui-material/src/Autocomplete/Autocomplete.test.js
+++ b/packages/mui-material/src/Autocomplete/Autocomplete.test.js
@@ -59,20 +59,50 @@ describe('', () => {
testStateOverrides: { prop: 'fullWidth', value: true, styleKey: 'fullWidth' },
refInstanceof: window.HTMLDivElement,
testComponentPropWith: 'div',
- testLegacyComponentsProp: true,
slots: {
- clearIndicator: { expectedClassName: classes.clearIndicator },
+ listbox: { expectedClassName: classes.listbox },
paper: { expectedClassName: classes.paper },
- popper: { expectedClassName: classes.popper },
+ popper: { expectedClassName: classes.popper, testWithElement: null },
+ },
+ skip: ['componentProp', 'componentsProp', 'reactTestRenderer'],
+ }),
+ );
+
+ describeConformance(
+ }
+ />,
+ () => ({
+ classes,
+ render,
+ muiName: 'MuiAutocomplete',
+ slots: {
+ clearIndicator: { expectedClassName: classes.clearIndicator },
popupIndicator: { expectedClassName: classes.popupIndicator },
},
- skip: [
- 'componentProp',
- 'componentsProp',
- 'slotsProp',
- 'reactTestRenderer',
- 'slotPropsCallback', // not supported yet
- ],
+ only: ['slotPropsProp'],
+ }),
+ );
+
+ describeConformance(
+ }
+ />,
+ () => ({
+ classes,
+ render,
+ muiName: 'MuiAutocomplete',
+ slots: {
+ chip: {},
+ },
+ only: ['slotPropsProp'],
}),
);