我创建了一个组件,该组件显示带有子组件的模式视图。单击该按钮即可显示该视图。
import React, { useRef, useState } from 'react';
import PropTypes from 'prop-types';
import { BsThreeDots } from 'react-icons/bs';
import useVisibleHandler from '~/helpers/hooks/useVisibleHandler';
import {
Container,
ButtonWrapper,
ActionList,
ActionContainer,
} from './styles';
export default function ActionsMenu({ children }) {
const actionsMenuRef = useRef();
const [visible, setVisible] = useState(false);
useVisibleHandler(actionsMenuRef, () => {
if (visible) {
setVisible(false);
}
});
function handleToggleVisible() {
setVisible(!visible);
}
return (
<Container data-testid="actions-menu-container" ref={actionsMenuRef}>
<ButtonWrapper data-testid="button-wrapper" onClick={handleToggleVisible}>
<BsThreeDots size={22} color="#b2b2b2" />
</ButtonWrapper>
<ActionList data-testid="action-list" visible={visible}>
<ActionContainer>{children}</ActionContainer>
</ActionList>
</Container>
);
}
ActionsMenu.propTypes = {
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
PropTypes.node,
]).isRequired,
};
我已通过ref将事件监听器添加到此组件中,以供鼠标离开。
import { useEffect } from 'react';
export default function useVisibleHandler(ref, handler, modal = false) {
const handleHideDropdown = (event) => {
if (event.key === 'Escape') {
handler();
}
};
const handleClick = (e) => {
if (ref.current && !ref.current.contains(e.target)) {
handler();
}
};
useEffect(() => {
document.addEventListener('keydown', handleHideDropdown, true);
document.addEventListener('click', handleClick, true);
if (!modal) {
document.addEventListener('mouseleave', handleClick, true);
}
return () => {
document.removeEventListener('keydown', handleHideDropdown, true);
document.removeEventListener('click', handleClick, true);
if (!modal) {
document.removeEventListener('mouseleave', handleClick, true);
}
};
});
}
我正在尝试用酶和Jest创建一个测试,以检查鼠标离开时视图是否不再可见。 这是我尝试实现的方法,但似乎无法找出如何有效运行鼠标离开处理程序,以便事件真正发生的方法。
import React from 'react';
import { render } from '@testing-library/react';
import { mount } from 'enzyme';
import ActionsMenu from '~/components/ActionsMenu';
describe('ActionsMenu component', () => {
it('Should close on mouseleave', () => {
const actionsMenuWrapper = mount(
<ActionsMenu>
<p>Test</p>
</ActionsMenu>
);
const buttonWrapper = actionsMenuWrapper
.find({
'data-testid': 'button-wrapper',
})
.at(0);
expect(buttonWrapper.length).toBe(1);
buttonWrapper.simulate('click');
let actionList = actionsMenuWrapper
.find({
'data-testid': 'action-list',
})
.at(0);
expect(actionList.length).toBe(1);
expect(actionList.props().visible).toBeTruthy();
let actionsMenuContainer = actionsMenuWrapper
.find({ 'data-testid': 'actions-menu-container' })
.at(0);
actionsMenuWrapper.update();
actionsMenuContainer.simulate('mouseenter');
actionsMenuContainer = actionsMenuWrapper
.find({ 'data-testid': 'actions-menu-container' })
.at(0);
actionsMenuContainer.simulate('mouseleave');
actionList = actionsMenuWrapper
.find({
'data-testid': 'action-list',
})
.at(0);
expect(actionList.props().visible).toBeFalsy();
});
});