Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[core] Remove useIsFocusVisible util #42467

Merged
merged 9 commits into from
Jul 1, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion packages/mui-base/src/Menu/Menu.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -618,7 +618,12 @@ describe('<Menu />', () => {
});
});

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();
Expand Down
7 changes: 6 additions & 1 deletion packages/mui-base/src/Select/Select.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1265,7 +1265,12 @@ describe('<Select />', () => {
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();
Expand Down
7 changes: 6 additions & 1 deletion packages/mui-base/src/Slider/Slider.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,12 @@ describe('<Slider />', () => {
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(
<Slider
defaultValue={[20, 40]}
Expand Down
7 changes: 6 additions & 1 deletion packages/mui-base/src/useButton/useButton.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,12 @@ describe('useButton', () => {
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;
}
Expand Down
22 changes: 4 additions & 18 deletions packages/mui-base/src/useButton/useButton.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -38,22 +38,11 @@ export function useButton(parameters: UseButtonParameters = {}): UseButtonReturn

const [active, setActive] = React.useState<boolean>(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',
Expand All @@ -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);
}

Expand All @@ -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);
}
Expand Down Expand Up @@ -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<HTMLButtonElement>['type'];
Expand Down
17 changes: 4 additions & 13 deletions packages/mui-base/src/useSlider/useSlider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -265,32 +265,23 @@ 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<HTMLSpanElement>();
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);
otherHandlers?.onFocus?.(event);
};
const createHandleHiddenInputBlur =
(otherHandlers: EventHandlers) => (event: React.FocusEvent) => {
handleBlurVisible(event);
if (isFocusVisibleRef.current === false) {
if (!isFocusVisible(event.target)) {
setFocusedThumbIndex(-1);
}
setOpen(-1);
Expand Down
7 changes: 6 additions & 1 deletion packages/mui-base/src/useSwitch/useSwitch.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
22 changes: 4 additions & 18 deletions packages/mui-base/src/useSwitch/useSwitch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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<HTMLInputElement | null>(null);

const createHandleFocus =
Expand All @@ -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);
}
Expand All @@ -90,17 +78,15 @@ export function useSwitch(props: UseSwitchParameters): UseSwitchReturnValue {
const createHandleBlur =
(otherProps: React.InputHTMLAttributes<HTMLInputElement>) =>
(event: React.FocusEvent<HTMLInputElement>) => {
handleBlurVisible(event);

if (isFocusVisibleRef.current === false) {
if (!isFocusVisible(event.target)) {
setFocusVisible(false);
}

onBlur?.(event);
otherProps.onBlur?.(event);
};

const handleInputRef = useForkRef(focusVisibleRef, inputRef);
const handleInputRef = useForkRef(inputRef);

const getInputProps: UseSwitchReturnValue['getInputProps'] = (otherProps = {}) => ({
checked: checkedProp,
Expand Down
7 changes: 6 additions & 1 deletion packages/mui-joy/src/Link/Link.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,12 @@ describe('<Link />', () => {
});

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(<Link href="/">Home</Link>);
const anchor = container.querySelector('a');

Expand Down
16 changes: 4 additions & 12 deletions packages/mui-joy/src/Link/Link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ 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';
Expand Down Expand Up @@ -216,26 +216,18 @@ 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<boolean>(false);
const handleRef = useForkRef(ref, focusVisibleRef) as React.Ref<HTMLAnchorElement>;
const handleRef = useForkRef(ref) as React.Ref<HTMLAnchorElement>;
const handleBlur = (event: React.FocusEvent<HTMLAnchorElement>) => {
handleBlurVisible(event);
if (isFocusVisibleRef.current === false) {
if (!isFocusVisible(event.target)) {
setFocusVisible(false);
}
if (onBlur) {
onBlur(event);
}
};
const handleFocus = (event: React.FocusEvent<HTMLAnchorElement>) => {
handleFocusVisible(event);
if (isFocusVisibleRef.current === true) {
if (isFocusVisible(event.target)) {
setFocusVisible(true);
}
if (onFocus) {
Expand Down
7 changes: 6 additions & 1 deletion packages/mui-joy/src/ListItemButton/ListItemButton.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,12 @@ describe('Joy <ListItemButton />', () => {
});

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(<ListItemButton />);
const button = getByRole('button');

Expand Down
16 changes: 4 additions & 12 deletions packages/mui-joy/src/Tooltip/Tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -333,18 +333,11 @@ const Tooltip = React.forwardRef(function Tooltip(inProps, ref) {
});
};

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<HTMLElement> | React.MouseEvent<HTMLElement>) => {
handleBlurVisible(event as React.FocusEvent<HTMLElement>);
if (isFocusVisibleRef.current === false) {
if (!isFocusVisible(event.target as EventTarget & Element)) {
setChildIsFocusVisible(false);
handleMouseLeave(event as React.MouseEvent<HTMLElement>);
}
Expand All @@ -358,8 +351,7 @@ const Tooltip = React.forwardRef(function Tooltip(inProps, ref) {
setChildNode(event.currentTarget);
}

handleFocusVisible(event as React.FocusEvent<HTMLElement>);
if (isFocusVisibleRef.current === true) {
if (!isFocusVisible(event.target as EventTarget & Element)) {
setChildIsFocusVisible(true);
handleMouseOver(event as React.MouseEvent<HTMLElement>);
}
Expand Down Expand Up @@ -423,7 +415,7 @@ const Tooltip = React.forwardRef(function Tooltip(inProps, ref) {
}, [handleClose, open]);

const handleUseRef = useForkRef(setChildNode, ref);
const handleFocusRef = useForkRef<Element>(focusVisibleRef, handleUseRef);
const handleFocusRef = useForkRef<Element>(handleUseRef);
const handleRef = useForkRef(
(children as unknown as { ref: React.Ref<HTMLElement> }).ref,
handleFocusRef,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,12 @@ describe('<AccordionSummary />', () => {
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(<AccordionSummary onFocusVisible={handleFocusVisible} />);
// simulate pointer device
Expand Down
14 changes: 12 additions & 2 deletions packages/mui-material/src/Button/Button.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -582,7 +582,12 @@ describe('<Button />', () => {
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(
<Button TouchRippleProps={{ classes: { ripplePulsate: 'pulsate-focus-visible' } }}>
Hello World
Expand All @@ -598,7 +603,12 @@ describe('<Button />', () => {
expect(button.querySelector('.pulsate-focus-visible')).not.to.equal(null);
});

it('can disable the focusRipple', () => {
it('can disable the focusRipple', function test() {
if (/jsdom/.test(window.navigator.userAgent)) {
// JSDOM doesn't support :focus-visible
this.skip();
}

const { getByRole } = render(
<Button
disableFocusRipple
Expand Down
Loading
Loading