diff --git a/packages/mui-base/src/Menu/Menu.test.tsx b/packages/mui-base/src/Menu/Menu.test.tsx
index 325e1b601062c3..214fff4420c2b9 100644
--- a/packages/mui-base/src/Menu/Menu.test.tsx
+++ b/packages/mui-base/src/Menu/Menu.test.tsx
@@ -618,7 +618,12 @@ describe('
', () => {
});
});
- it('perf: does not rerender menu items unnecessarily', async () => {
+ it('perf: does not rerender menu items unnecessarily', async function test() {
+ if (/jsdom/.test(window.navigator.userAgent)) {
+ // JSDOM doesn't support :focus-visible
+ this.skip();
+ }
+
const renderItem1Spy = spy();
const renderItem2Spy = spy();
const renderItem3Spy = spy();
diff --git a/packages/mui-base/src/Select/Select.test.tsx b/packages/mui-base/src/Select/Select.test.tsx
index d3ec34e600d7c0..d401b28bf1616a 100644
--- a/packages/mui-base/src/Select/Select.test.tsx
+++ b/packages/mui-base/src/Select/Select.test.tsx
@@ -1265,7 +1265,12 @@ describe('', () => {
expect(selectButton).to.have.text('1, 2');
});
- it('perf: does not rerender options unnecessarily', async () => {
+ it('perf: does not rerender options unnecessarily', async function test() {
+ if (/jsdom/.test(window.navigator.userAgent)) {
+ // JSDOM doesn't support :focus-visible
+ this.skip();
+ }
+
const renderOption1Spy = spy();
const renderOption2Spy = spy();
const renderOption3Spy = spy();
diff --git a/packages/mui-base/src/Slider/Slider.test.tsx b/packages/mui-base/src/Slider/Slider.test.tsx
index 0c426a2544f2b1..fabd03e2c765ab 100644
--- a/packages/mui-base/src/Slider/Slider.test.tsx
+++ b/packages/mui-base/src/Slider/Slider.test.tsx
@@ -368,7 +368,12 @@ describe('', () => {
expect(screen.getByTestId('value-label')).to.have.text('20');
});
- it('should provide focused state to the slotProps.thumb', () => {
+ it('should provide focused state to the slotProps.thumb', function test() {
+ if (/jsdom/.test(window.navigator.userAgent)) {
+ // JSDOM doesn't support :focus-visible
+ this.skip();
+ }
+
const { getByTestId } = render(
{
expect(handleClickExternal.callCount).to.equal(0);
});
- it('handles onFocusVisible and does not include it in the root props', () => {
+ it('handles onFocusVisible and does not include it in the root props', function test() {
+ if (/jsdom/.test(window.navigator.userAgent)) {
+ // JSDOM doesn't support :focus-visible
+ this.skip();
+ }
+
interface WithFocusVisibleHandler {
onFocusVisible: React.FocusEventHandler;
}
diff --git a/packages/mui-base/src/useButton/useButton.ts b/packages/mui-base/src/useButton/useButton.ts
index f8520a751abdb8..d87c184f4755cb 100644
--- a/packages/mui-base/src/useButton/useButton.ts
+++ b/packages/mui-base/src/useButton/useButton.ts
@@ -2,7 +2,7 @@
import * as React from 'react';
import {
unstable_useForkRef as useForkRef,
- unstable_useIsFocusVisible as useIsFocusVisible,
+ unstable_isFocusVisible as isFocusVisible,
} from '@mui/utils';
import {
UseButtonParameters,
@@ -38,22 +38,11 @@ export function useButton(parameters: UseButtonParameters = {}): UseButtonReturn
const [active, setActive] = React.useState(false);
- const {
- isFocusVisibleRef,
- onFocus: handleFocusVisible,
- onBlur: handleBlurVisible,
- ref: focusVisibleRef,
- } = useIsFocusVisible();
-
const [focusVisible, setFocusVisible] = React.useState(false);
if (disabled && !focusableWhenDisabled && focusVisible) {
setFocusVisible(false);
}
- React.useEffect(() => {
- isFocusVisibleRef.current = focusVisible;
- }, [focusVisible, isFocusVisibleRef]);
-
const [rootElementName, updateRootElementName] = useRootElementName({
rootElementName: rootElementNameProp ?? (href || to ? 'a' : undefined),
componentName: 'Button',
@@ -68,9 +57,7 @@ export function useButton(parameters: UseButtonParameters = {}): UseButtonReturn
};
const createHandleBlur = (otherHandlers: EventHandlers) => (event: React.FocusEvent) => {
- handleBlurVisible(event);
-
- if (isFocusVisibleRef.current === false) {
+ if (!isFocusVisible(event.target)) {
setFocusVisible(false);
}
@@ -84,8 +71,7 @@ export function useButton(parameters: UseButtonParameters = {}): UseButtonReturn
buttonRef.current = event.currentTarget;
}
- handleFocusVisible(event);
- if (isFocusVisibleRef.current === true) {
+ if (isFocusVisible(event.target)) {
setFocusVisible(true);
otherHandlers.onFocusVisible?.(event);
}
@@ -176,7 +162,7 @@ export function useButton(parameters: UseButtonParameters = {}): UseButtonReturn
}
};
- const handleRef = useForkRef(updateRootElementName, externalRef, focusVisibleRef, buttonRef);
+ const handleRef = useForkRef(updateRootElementName, externalRef, buttonRef);
interface AdditionalButtonProps {
type?: React.ButtonHTMLAttributes['type'];
diff --git a/packages/mui-base/src/useSlider/useSlider.ts b/packages/mui-base/src/useSlider/useSlider.ts
index 94869cc604debb..37dbea1d3fbec0 100644
--- a/packages/mui-base/src/useSlider/useSlider.ts
+++ b/packages/mui-base/src/useSlider/useSlider.ts
@@ -6,7 +6,7 @@ import {
unstable_useEnhancedEffect as useEnhancedEffect,
unstable_useEventCallback as useEventCallback,
unstable_useForkRef as useForkRef,
- unstable_useIsFocusVisible as useIsFocusVisible,
+ unstable_isFocusVisible as isFocusVisible,
visuallyHidden,
clamp,
} from '@mui/utils';
@@ -265,23 +265,15 @@ export function useSlider(parameters: UseSliderParameters): UseSliderReturnValue
const marksValues = (marks as Mark[]).map((mark: Mark) => mark.value);
- const {
- isFocusVisibleRef,
- onBlur: handleBlurVisible,
- onFocus: handleFocusVisible,
- ref: focusVisibleRef,
- } = useIsFocusVisible();
const [focusedThumbIndex, setFocusedThumbIndex] = React.useState(-1);
const sliderRef = React.useRef();
- const handleFocusRef = useForkRef(focusVisibleRef, sliderRef);
- const handleRef = useForkRef(ref, handleFocusRef);
+ const handleRef = useForkRef(ref, sliderRef);
const createHandleHiddenInputFocus =
(otherHandlers: EventHandlers) => (event: React.FocusEvent) => {
const index = Number(event.currentTarget.getAttribute('data-index'));
- handleFocusVisible(event);
- if (isFocusVisibleRef.current === true) {
+ if (isFocusVisible(event.target)) {
setFocusedThumbIndex(index);
}
setOpen(index);
@@ -289,8 +281,7 @@ export function useSlider(parameters: UseSliderParameters): UseSliderReturnValue
};
const createHandleHiddenInputBlur =
(otherHandlers: EventHandlers) => (event: React.FocusEvent) => {
- handleBlurVisible(event);
- if (isFocusVisibleRef.current === false) {
+ if (!isFocusVisible(event.target)) {
setFocusedThumbIndex(-1);
}
setOpen(-1);
diff --git a/packages/mui-base/src/useSwitch/useSwitch.test.tsx b/packages/mui-base/src/useSwitch/useSwitch.test.tsx
index 1a9bb0853af021..8496dfa321293a 100644
--- a/packages/mui-base/src/useSwitch/useSwitch.test.tsx
+++ b/packages/mui-base/src/useSwitch/useSwitch.test.tsx
@@ -71,7 +71,12 @@ describe('useSwitch', () => {
expect(handleChange.callCount).to.equal(1);
});
- it('should call focus event handlers if focus events are fired', () => {
+ it('should call focus event handlers if focus events are fired', function test() {
+ if (/jsdom/.test(window.navigator.userAgent)) {
+ // JSDOM doesn't support :focus-visible
+ this.skip();
+ }
+
const handleBlur = spy();
const handleFocus = spy();
const handleFocusVisible = spy();
diff --git a/packages/mui-base/src/useSwitch/useSwitch.ts b/packages/mui-base/src/useSwitch/useSwitch.ts
index ed30f150321346..b63ca86d7cfb27 100644
--- a/packages/mui-base/src/useSwitch/useSwitch.ts
+++ b/packages/mui-base/src/useSwitch/useSwitch.ts
@@ -3,7 +3,7 @@ import * as React from 'react';
import {
unstable_useControlled as useControlled,
unstable_useForkRef as useForkRef,
- unstable_useIsFocusVisible as useIsFocusVisible,
+ unstable_isFocusVisible as isFocusVisible,
} from '@mui/utils';
import { UseSwitchParameters, UseSwitchReturnValue } from './useSwitch.types';
@@ -51,22 +51,11 @@ export function useSwitch(props: UseSwitchParameters): UseSwitchReturnValue {
otherProps.onChange?.(event);
};
- const {
- isFocusVisibleRef,
- onBlur: handleBlurVisible,
- onFocus: handleFocusVisible,
- ref: focusVisibleRef,
- } = useIsFocusVisible();
-
const [focusVisible, setFocusVisible] = React.useState(false);
if (disabled && focusVisible) {
setFocusVisible(false);
}
- React.useEffect(() => {
- isFocusVisibleRef.current = focusVisible;
- }, [focusVisible, isFocusVisibleRef]);
-
const inputRef = React.useRef(null);
const createHandleFocus =
@@ -77,8 +66,7 @@ export function useSwitch(props: UseSwitchParameters): UseSwitchReturnValue {
inputRef.current = event.currentTarget;
}
- handleFocusVisible(event);
- if (isFocusVisibleRef.current === true) {
+ if (isFocusVisible(event.target)) {
setFocusVisible(true);
onFocusVisible?.(event);
}
@@ -90,9 +78,7 @@ export function useSwitch(props: UseSwitchParameters): UseSwitchReturnValue {
const createHandleBlur =
(otherProps: React.InputHTMLAttributes) =>
(event: React.FocusEvent) => {
- handleBlurVisible(event);
-
- if (isFocusVisibleRef.current === false) {
+ if (!isFocusVisible(event.target)) {
setFocusVisible(false);
}
@@ -100,7 +86,7 @@ export function useSwitch(props: UseSwitchParameters): UseSwitchReturnValue {
otherProps.onBlur?.(event);
};
- const handleInputRef = useForkRef(focusVisibleRef, inputRef);
+ const handleInputRef = useForkRef(inputRef);
const getInputProps: UseSwitchReturnValue['getInputProps'] = (otherProps = {}) => ({
checked: checkedProp,
diff --git a/packages/mui-joy/src/Link/Link.test.tsx b/packages/mui-joy/src/Link/Link.test.tsx
index 68da3bea32c381..133522e41b49b6 100644
--- a/packages/mui-joy/src/Link/Link.test.tsx
+++ b/packages/mui-joy/src/Link/Link.test.tsx
@@ -75,7 +75,12 @@ describe('', () => {
});
describe('keyboard focus', () => {
- it('should add the focusVisible class when focused', () => {
+ it('should add the focusVisible class when focused', function test() {
+ if (/jsdom/.test(window.navigator.userAgent)) {
+ // JSDOM doesn't support :focus-visible
+ this.skip();
+ }
+
const { container } = render(Home);
const anchor = container.querySelector('a');
diff --git a/packages/mui-joy/src/Link/Link.tsx b/packages/mui-joy/src/Link/Link.tsx
index bac2fbe4f315bc..afe985b6128d6c 100644
--- a/packages/mui-joy/src/Link/Link.tsx
+++ b/packages/mui-joy/src/Link/Link.tsx
@@ -5,8 +5,7 @@ import { unstable_composeClasses as composeClasses } from '@mui/base';
import { OverridableComponent } from '@mui/types';
import {
unstable_capitalize as capitalize,
- unstable_useForkRef as useForkRef,
- unstable_useIsFocusVisible as useIsFocusVisible,
+ unstable_isFocusVisible as isFocusVisible,
unstable_isMuiElement as isMuiElement,
} from '@mui/utils';
import { unstable_extendSxProp as extendSxProp } from '@mui/system';
@@ -216,17 +215,9 @@ const Link = React.forwardRef(function Link(inProps, ref) {
const level = nesting || inheriting ? inProps.level || 'inherit' : levelProp;
- const {
- isFocusVisibleRef,
- onBlur: handleBlurVisible,
- onFocus: handleFocusVisible,
- ref: focusVisibleRef,
- } = useIsFocusVisible();
const [focusVisible, setFocusVisible] = React.useState(false);
- const handleRef = useForkRef(ref, focusVisibleRef) as React.Ref;
const handleBlur = (event: React.FocusEvent) => {
- handleBlurVisible(event);
- if (isFocusVisibleRef.current === false) {
+ if (!isFocusVisible(event.target)) {
setFocusVisible(false);
}
if (onBlur) {
@@ -234,8 +225,7 @@ const Link = React.forwardRef(function Link(inProps, ref) {
}
};
const handleFocus = (event: React.FocusEvent) => {
- handleFocusVisible(event);
- if (isFocusVisibleRef.current === true) {
+ if (isFocusVisible(event.target)) {
setFocusVisible(true);
}
if (onFocus) {
@@ -263,7 +253,7 @@ const Link = React.forwardRef(function Link(inProps, ref) {
onBlur: handleBlur,
onFocus: handleFocus,
},
- ref: handleRef,
+ ref,
className: classes.root,
elementType: LinkRoot,
externalForwardedProps,
diff --git a/packages/mui-joy/src/ListItemButton/ListItemButton.test.tsx b/packages/mui-joy/src/ListItemButton/ListItemButton.test.tsx
index 0c40f7ad93923c..08f3eb26a2c20d 100644
--- a/packages/mui-joy/src/ListItemButton/ListItemButton.test.tsx
+++ b/packages/mui-joy/src/ListItemButton/ListItemButton.test.tsx
@@ -56,7 +56,12 @@ describe('Joy ', () => {
});
describe('prop: focusVisibleClassName', () => {
- it('should have focusVisible classes', () => {
+ it('should have focusVisible classes', function test() {
+ if (/jsdom/.test(window.navigator.userAgent)) {
+ // JSDOM doesn't support :focus-visible
+ this.skip();
+ }
+
const { getByRole } = render();
const button = getByRole('button');
diff --git a/packages/mui-joy/src/Tooltip/Tooltip.tsx b/packages/mui-joy/src/Tooltip/Tooltip.tsx
index b3c6e14d88324a..c6875348d03460 100644
--- a/packages/mui-joy/src/Tooltip/Tooltip.tsx
+++ b/packages/mui-joy/src/Tooltip/Tooltip.tsx
@@ -7,7 +7,7 @@ import {
unstable_useControlled as useControlled,
unstable_useEventCallback as useEventCallback,
unstable_useForkRef as useForkRef,
- unstable_useIsFocusVisible as useIsFocusVisible,
+ unstable_isFocusVisible as isFocusVisible,
unstable_useId as useId,
unstable_useTimeout as useTimeout,
unstable_Timeout as Timeout,
@@ -274,7 +274,7 @@ const Tooltip = React.forwardRef(function Tooltip(inProps, ref) {
React.useEffect(() => stopTouchInteraction, [stopTouchInteraction]);
- const handleOpen = (event: React.MouseEvent) => {
+ const handleOpen = (event: React.SyntheticEvent) => {
hystersisTimer.clear();
hystersisOpen = true;
@@ -303,7 +303,7 @@ const Tooltip = React.forwardRef(function Tooltip(inProps, ref) {
});
});
- const handleMouseOver = (event: React.MouseEvent) => {
+ const handleMouseOver = (event: React.SyntheticEvent) => {
if (ignoreNonTouchEvents.current && event.type !== 'touchstart') {
return;
}
@@ -326,31 +326,24 @@ const Tooltip = React.forwardRef(function Tooltip(inProps, ref) {
}
};
- const handleMouseLeave = (event: React.MouseEvent) => {
+ const handleMouseLeave = (event: React.SyntheticEvent) => {
enterTimer.clear();
leaveTimer.start(leaveDelay, () => {
handleClose(event);
});
};
- const {
- isFocusVisibleRef,
- onBlur: handleBlurVisible,
- onFocus: handleFocusVisible,
- ref: focusVisibleRef,
- } = useIsFocusVisible();
// We don't necessarily care about the focusVisible state (which is safe to access via ref anyway).
// We just need to re-render the Tooltip if the focus-visible state changes.
const [, setChildIsFocusVisible] = React.useState(false);
- const handleBlur = (event: React.FocusEvent | React.MouseEvent) => {
- handleBlurVisible(event as React.FocusEvent);
- if (isFocusVisibleRef.current === false) {
+ const handleBlur = (event: React.FocusEvent) => {
+ if (!isFocusVisible(event.target)) {
setChildIsFocusVisible(false);
- handleMouseLeave(event as React.MouseEvent);
+ handleMouseLeave(event);
}
};
- const handleFocus = (event: React.FocusEvent | React.MouseEvent) => {
+ const handleFocus = (event: React.FocusEvent) => {
// Workaround for https://github.com/facebook/react/issues/7769
// The autoFocus of React might trigger the event before the componentDidMount.
// We need to account for this eventuality.
@@ -358,10 +351,9 @@ const Tooltip = React.forwardRef(function Tooltip(inProps, ref) {
setChildNode(event.currentTarget);
}
- handleFocusVisible(event as React.FocusEvent);
- if (isFocusVisibleRef.current === true) {
+ if (isFocusVisible(event.target)) {
setChildIsFocusVisible(true);
- handleMouseOver(event as React.MouseEvent);
+ handleMouseOver(event);
}
};
@@ -423,10 +415,9 @@ const Tooltip = React.forwardRef(function Tooltip(inProps, ref) {
}, [handleClose, open]);
const handleUseRef = useForkRef(setChildNode, ref);
- const handleFocusRef = useForkRef(focusVisibleRef, handleUseRef);
const handleRef = useForkRef(
(children as unknown as { ref: React.Ref }).ref,
- handleFocusRef,
+ handleUseRef,
);
// There is no point in displaying an empty tooltip.
diff --git a/packages/mui-material/src/AccordionSummary/AccordionSummary.test.js b/packages/mui-material/src/AccordionSummary/AccordionSummary.test.js
index ae4d845e9ad07d..a8d1cf1aaa510d 100644
--- a/packages/mui-material/src/AccordionSummary/AccordionSummary.test.js
+++ b/packages/mui-material/src/AccordionSummary/AccordionSummary.test.js
@@ -98,7 +98,12 @@ describe('', () => {
expect(handleChange.callCount).to.equal(1);
});
- it('calls onFocusVisible if focused visibly', () => {
+ it('calls onFocusVisible if focused visibly', function test() {
+ if (/jsdom/.test(window.navigator.userAgent)) {
+ // JSDOM doesn't support :focus-visible
+ this.skip();
+ }
+
const handleFocusVisible = spy();
const { getByRole } = render();
// simulate pointer device
diff --git a/packages/mui-material/src/Button/Button.test.js b/packages/mui-material/src/Button/Button.test.js
index 0b887e085482df..47fa7b0607c42e 100644
--- a/packages/mui-material/src/Button/Button.test.js
+++ b/packages/mui-material/src/Button/Button.test.js
@@ -582,7 +582,12 @@ describe('', () => {
expect(button).to.have.class(classes.disableElevation);
});
- it('should have a focusRipple by default', () => {
+ it('should have a focusRipple by default', function test() {
+ if (/jsdom/.test(window.navigator.userAgent)) {
+ // JSDOM doesn't support :focus-visible
+ this.skip();
+ }
+
const { getByRole } = render(