diff --git a/docs/pages/api-docs/autocomplete.md b/docs/pages/api-docs/autocomplete.md
index dea6da27cf862a..3251614759305b 100644
--- a/docs/pages/api-docs/autocomplete.md
+++ b/docs/pages/api-docs/autocomplete.md
@@ -39,9 +39,9 @@ You can learn more about the difference by [reading this guide](/guides/minimizi
| disableClearable | bool | false | If `true`, the input can't be cleared. |
| disableCloseOnSelect | bool | false | If `true`, the popup won't close when a value is selected. |
| disabled | bool | false | If `true`, the input will be disabled. |
+| disabledItemsFocusable | bool | false | If `true`, will allow focus on disabled items. |
| disableListWrap | bool | false | If `true`, the list box in the popup will not wrap focus. |
| disablePortal | bool | false | Disable the portal behavior. The children stay within it's parent DOM hierarchy. |
-| enableFocusForDisabledItems | bool | false | If `true`, will allow focus on disabled items. |
| filterOptions | func | | A filter function that determines the options that are eligible.
**Signature:**
`function(options: T[], state: object) => undefined`
*options:* The options to render.
*state:* The state of the component. |
| filterSelectedOptions | bool | false | If `true`, hide the selected options from the list box. |
| forcePopupIcon | 'auto'
| bool | 'auto' | Force the visibility display of the popup icon. |
diff --git a/docs/pages/api-docs/menu-list.md b/docs/pages/api-docs/menu-list.md
index cfdf7e400b44f2..bd1a752faba64b 100644
--- a/docs/pages/api-docs/menu-list.md
+++ b/docs/pages/api-docs/menu-list.md
@@ -30,8 +30,8 @@ the focus is placed inside the component it is fully keyboard accessible.
| autoFocus | bool | false | If `true`, will focus the `[role="menu"]` container and move into tab order. |
| autoFocusItem | bool | false | If `true`, will focus the first menuitem if `variant="menu"` or selected item if `variant="selectedMenu"`. |
| children | node | | MenuList contents, normally `MenuItem`s. |
+| disabledItemsFocusable | bool | false | If `true`, will allow focus on disabled items. |
| disableListWrap | bool | false | If `true`, the menu items will not wrap focus. |
-| enableFocusForDisabledItems | bool | false | If `true`, will allow focus on disabled items. |
| variant | 'menu'
| 'selectedMenu' | 'selectedMenu' | The variant to use. Use `menu` to prevent selected items from impacting the initial focus and the vertical alignment relative to the anchor element. |
The `ref` is forwarded to the root element.
diff --git a/docs/src/pages/components/autocomplete/LimitTags.js b/docs/src/pages/components/autocomplete/LimitTags.js
index 8002327845d082..7f2bd393679983 100644
--- a/docs/src/pages/components/autocomplete/LimitTags.js
+++ b/docs/src/pages/components/autocomplete/LimitTags.js
@@ -21,7 +21,7 @@ export default function LimitTags() {
option.title}
defaultValue={[top100Films[13], top100Films[12], top100Films[11]]}
diff --git a/docs/src/pages/components/autocomplete/LimitTags.tsx b/docs/src/pages/components/autocomplete/LimitTags.tsx
index 8c7c8ea8a5af8a..e4dfbb476a8ce6 100644
--- a/docs/src/pages/components/autocomplete/LimitTags.tsx
+++ b/docs/src/pages/components/autocomplete/LimitTags.tsx
@@ -23,7 +23,7 @@ export default function LimitTags() {
option.title}
defaultValue={[top100Films[13], top100Films[12], top100Films[11]]}
diff --git a/packages/material-ui-lab/src/Autocomplete/Autocomplete.js b/packages/material-ui-lab/src/Autocomplete/Autocomplete.js
index 50d9161ee45ec5..c7b1a2c1ae1552 100644
--- a/packages/material-ui-lab/src/Autocomplete/Autocomplete.js
+++ b/packages/material-ui-lab/src/Autocomplete/Autocomplete.js
@@ -251,9 +251,9 @@ const Autocomplete = React.forwardRef(function Autocomplete(props, ref) {
disableClearable = false,
disableCloseOnSelect = false,
disabled = false,
+ disabledItemsFocusable = false,
disableListWrap = false,
disablePortal = false,
- enableFocusForDisabledItems = false,
filterOptions,
filterSelectedOptions = false,
forcePopupIcon = 'auto',
@@ -578,6 +578,10 @@ Autocomplete.propTypes = {
* If `true`, the input will be disabled.
*/
disabled: PropTypes.bool,
+ /**
+ * If `true`, will allow focus on disabled items.
+ */
+ disabledItemsFocusable: PropTypes.bool,
/**
* If `true`, the list box in the popup will not wrap focus.
*/
@@ -587,10 +591,6 @@ Autocomplete.propTypes = {
* The children stay within it's parent DOM hierarchy.
*/
disablePortal: PropTypes.bool,
- /**
- * If `true`, will allow focus on disabled items.
- */
- enableFocusForDisabledItems: PropTypes.bool,
/**
* A filter function that determines the options that are eligible.
*
diff --git a/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.d.ts b/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.d.ts
index 9a6ec38e55ec77..821e062b11c9c0 100644
--- a/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.d.ts
+++ b/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.d.ts
@@ -67,13 +67,13 @@ export interface UseAutocompleteCommonProps {
*/
disableCloseOnSelect?: boolean;
/**
- * If `true`, the list box in the popup will not wrap focus.
+ * If `true`, will allow focus on disabled items.
*/
- disableListWrap?: boolean;
+ disabledItemsFocusable?: boolean;
/**
- * If `true`, will allow focus on disabled items.
+ * If `true`, the list box in the popup will not wrap focus.
*/
- enableFocusForDisabledItems?: boolean;
+ disableListWrap?: boolean;
/**
* A filter function that determines the options that are eligible.
*
@@ -184,6 +184,10 @@ export type AutocompleteCloseReason = 'toggleInput' | 'escape' | 'select-option'
export type AutocompleteInputChangeReason = 'input' | 'reset' | 'clear';
export interface UseAutocompleteMultipleProps extends UseAutocompleteCommonProps {
+ /**
+ * The default input value. Use when the component is not controlled.
+ */
+ defaultValue?: T[];
/**
* If `true`, `value` must be an array and the menu will support multiple selections.
*/
@@ -195,10 +199,6 @@ export interface UseAutocompleteMultipleProps extends UseAutocompleteCommonPr
* You can customize the equality behavior with the `getOptionSelected` prop.
*/
value?: T[];
- /**
- * The default input value. Use when the component is not controlled.
- */
- defaultValue?: T[];
/**
* Callback fired when the value changes.
*
@@ -215,6 +215,10 @@ export interface UseAutocompleteMultipleProps extends UseAutocompleteCommonPr
}
export interface UseAutocompleteSingleProps extends UseAutocompleteCommonProps {
+ /**
+ * The default input value. Use when the component is not controlled.
+ */
+ defaultValue?: T;
/**
* If `true`, `value` must be an array and the menu will support multiple selections.
*/
@@ -226,10 +230,6 @@ export interface UseAutocompleteSingleProps extends UseAutocompleteCommonProp
* You can customize the equality behavior with the `getOptionSelected` prop.
*/
value?: T | null;
- /**
- * The default input value. Use when the component is not controlled.
- */
- defaultValue?: T;
/**
* Callback fired when the value changes.
*
diff --git a/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js b/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js
index f50cecc5563c7f..2c98da8abca486 100644
--- a/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js
+++ b/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.js
@@ -73,8 +73,8 @@ export default function useAutocomplete(props) {
defaultValue = props.multiple ? [] : null,
disableClearable = false,
disableCloseOnSelect = false,
+ disabledItemsFocusable = false,
disableListWrap = false,
- enableFocusForDisabledItems = false,
filterOptions = defaultFilterOptions,
filterSelectedOptions = false,
freeSolo = false,
@@ -313,11 +313,12 @@ export default function useAutocomplete(props) {
const option = listboxRef.current.querySelector(`[data-option-index="${nextFocus}"]`);
// Same logic as MenuList.js
- const nextFocusDisabled = enableFocusForDisabledItems
+ const nextFocusDisabled = disabledItemsFocusable
? false
: option && (option.disabled || option.getAttribute('aria-disabled') === 'true');
if ((option && !option.hasAttribute('tabindex')) || nextFocusDisabled) {
+ // Move to the next element.
nextFocus += direction === 'next' ? 1 : -1;
} else {
return nextFocus;
@@ -641,9 +642,16 @@ export default function useAutocomplete(props) {
break;
}
if (highlightedIndexRef.current !== -1 && popupOpen) {
+ const option = filteredOptions[highlightedIndexRef.current];
+ const disabled = getOptionDisabled ? getOptionDisabled(option) : false;
+
+ if (disabled) {
+ return;
+ }
+
// We don't want to validate the form.
event.preventDefault();
- selectNewValue(event, filteredOptions[highlightedIndexRef.current], 'select-option');
+ selectNewValue(event, option, 'select-option');
// Move the selection to the end.
if (autoComplete) {
@@ -1012,13 +1020,13 @@ useAutocomplete.propTypes = {
*/
disableCloseOnSelect: PropTypes.bool,
/**
- * If `true`, the list box in the popup will not wrap focus.
+ * If `true`, will allow focus on disabled items.
*/
- disableListWrap: PropTypes.bool,
+ disabledItemsFocusable: PropTypes.bool,
/**
- * If `true`, will allow focus on disabled items.
+ * If `true`, the list box in the popup will not wrap focus.
*/
- enableFocusForDisabledItems: PropTypes.bool,
+ disableListWrap: PropTypes.bool,
/**
* A filter function that determins the options that are eligible.
*
diff --git a/packages/material-ui/src/ButtonBase/ButtonBase.js b/packages/material-ui/src/ButtonBase/ButtonBase.js
index c872e7104b20d1..2d3636abed3862 100644
--- a/packages/material-ui/src/ButtonBase/ButtonBase.js
+++ b/packages/material-ui/src/ButtonBase/ButtonBase.js
@@ -159,7 +159,7 @@ const ButtonBase = React.forwardRef(function ButtonBase(props, ref) {
false,
);
- const handleFocus = useEventCallback(event => {
+ const handleFocus = useEventCallback((event) => {
// Fix for https://github.com/facebook/react/issues/7769
if (!buttonRef.current) {
buttonRef.current = event.currentTarget;
@@ -212,7 +212,12 @@ const ButtonBase = React.forwardRef(function ButtonBase(props, ref) {
}
// Keyboard accessibility for non interactive elements
- if (event.target === event.currentTarget && isNonNativeButton() && event.key === 'Enter') {
+ if (
+ event.target === event.currentTarget &&
+ isNonNativeButton() &&
+ event.key === 'Enter' &&
+ !disabled
+ ) {
event.preventDefault();
if (onClick) {
onClick(event);
diff --git a/packages/material-ui/src/MenuList/MenuList.d.ts b/packages/material-ui/src/MenuList/MenuList.d.ts
index 8d91ab6ed7643e..0033457b50670b 100644
--- a/packages/material-ui/src/MenuList/MenuList.d.ts
+++ b/packages/material-ui/src/MenuList/MenuList.d.ts
@@ -16,6 +16,10 @@ export interface MenuListProps extends StandardProps integration', () => {
});
});
- it('should skip divider and disabled menu item when enableFocusForDisabledItems=false', () => {
+ it('should skip divider and disabled menu item', () => {
const { getAllByRole } = render(
-
+
@@ -325,90 +325,28 @@ describe(' integration', () => {
const menuitems = getAllByRole('menuitem');
fireEvent.keyDown(document.activeElement, { key: 'ArrowDown' });
-
expect(menuitems[0]).to.have.focus;
-
fireEvent.keyDown(document.activeElement, { key: 'ArrowDown' });
-
expect(menuitems[1]).to.have.focus;
-
fireEvent.keyDown(document.activeElement, { key: 'ArrowDown' });
-
expect(menuitems[3]).to.have.focus;
-
fireEvent.keyDown(document.activeElement, { key: 'ArrowDown' });
-
expect(menuitems[0]).to.have.focus;
// and ArrowUp again
-
fireEvent.keyDown(document.activeElement, { key: 'ArrowUp' });
-
expect(menuitems[3]).to.have.focus;
-
fireEvent.keyDown(document.activeElement, { key: 'ArrowUp' });
-
expect(menuitems[1]).to.have.focus;
-
fireEvent.keyDown(document.activeElement, { key: 'ArrowUp' });
-
expect(menuitems[0]).to.have.focus;
-
fireEvent.keyDown(document.activeElement, { key: 'ArrowUp' });
-
expect(menuitems[3]).to.have.focus;
});
- it('should skip divider', () => {
- const { getAllByRole } = render(
-
-
-
-
-
-
- ,
- );
- const menuitems = getAllByRole('menuitem');
-
- fireEvent.keyDown(document.activeElement, { key: 'ArrowDown' });
-
- expect(menuitems[0]).to.have.focus;
-
- fireEvent.keyDown(document.activeElement, { key: 'ArrowDown' });
-
- expect(menuitems[1]).to.have.focus;
-
- fireEvent.keyDown(document.activeElement, { key: 'ArrowDown' });
-
- expect(menuitems[2]).to.have.focus;
-
- fireEvent.keyDown(document.activeElement, { key: 'ArrowDown' });
-
- expect(menuitems[0]).to.have.focus;
-
- // and ArrowUp again
-
- fireEvent.keyDown(document.activeElement, { key: 'ArrowUp' });
-
- expect(menuitems[2]).to.have.focus;
-
- fireEvent.keyDown(document.activeElement, { key: 'ArrowUp' });
-
- expect(menuitems[1]).to.have.focus;
-
- fireEvent.keyDown(document.activeElement, { key: 'ArrowUp' });
-
- expect(menuitems[0]).to.have.focus;
-
- fireEvent.keyDown(document.activeElement, { key: 'ArrowUp' });
-
- expect(menuitems[2]).to.have.focus;
- });
-
it('should stay on a single item if it is the only focusable one', () => {
const { getAllByRole } = render(
-
+
@@ -418,29 +356,20 @@ describe(' integration', () => {
const menuitems = getAllByRole('menuitem');
fireEvent.keyDown(document.activeElement, { key: 'ArrowDown' });
-
expect(menuitems[1]).to.have.focus;
-
fireEvent.keyDown(document.activeElement, { key: 'ArrowDown' });
-
expect(menuitems[1]).to.have.focus;
-
fireEvent.keyDown(document.activeElement, { key: 'ArrowDown' });
-
expect(menuitems[1]).to.have.focus;
-
fireEvent.keyDown(document.activeElement, { key: 'ArrowUp' });
-
expect(menuitems[1]).to.have.focus;
-
fireEvent.keyDown(document.activeElement, { key: 'ArrowUp' });
-
expect(menuitems[1]).to.have.focus;
});
- it('should keep focus on the menu if all items are disabled and enableFocusForDisabledItems=false', () => {
+ it('should keep focus on the menu if all items are disabled', () => {
const { getByRole } = render(
-
+
@@ -450,29 +379,20 @@ describe(' integration', () => {
const menu = getByRole('menu');
fireEvent.keyDown(document.activeElement, { key: 'Home' });
-
expect(menu).to.have.focus;
-
fireEvent.keyDown(document.activeElement, { key: 'ArrowDown' });
-
expect(menu).to.have.focus;
-
fireEvent.keyDown(document.activeElement, { key: 'ArrowDown' });
-
expect(menu).to.have.focus;
-
fireEvent.keyDown(document.activeElement, { key: 'End' });
-
expect(menu).to.have.focus;
-
fireEvent.keyDown(document.activeElement, { key: 'ArrowUp' });
-
expect(menu).to.have.focus;
});
- it('should allow focus on disabled items when enableFocusForDisabledItems=true', () => {
+ it('should allow focus on disabled items when disabledItemsFocusable=true', () => {
const { getAllByRole } = render(
-
+
@@ -483,23 +403,14 @@ describe(' integration', () => {
const menuitems = getAllByRole('menuitem');
fireEvent.keyDown(document.activeElement, { key: 'Home' });
-
expect(menuitems[0]).to.have.focus;
-
fireEvent.keyDown(document.activeElement, { key: 'ArrowDown' });
-
expect(menuitems[1]).to.have.focus;
-
fireEvent.keyDown(document.activeElement, { key: 'ArrowDown' });
-
expect(menuitems[2]).to.have.focus;
-
fireEvent.keyDown(document.activeElement, { key: 'End' });
-
expect(menuitems[3]).to.have.focus;
-
fireEvent.keyDown(document.activeElement, { key: 'ArrowUp' });
-
expect(menuitems[2]).to.have.focus;
});