Skip to content

Commit 6dfa366

Browse files
authored
fix(RAC): support hover events for Tabs (#6742)
support hover events for Tab
1 parent 826d9d4 commit 6dfa366

File tree

2 files changed

+35
-4
lines changed

2 files changed

+35
-4
lines changed

packages/react-aria-components/src/Tabs.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
* governing permissions and limitations under the License.
1111
*/
1212

13-
import {AriaLabelingProps, forwardRefType, Key, LinkDOMProps, RefObject} from '@react-types/shared';
13+
import {AriaLabelingProps, forwardRefType, HoverEvents, Key, LinkDOMProps, RefObject} from '@react-types/shared';
1414
import {AriaTabListProps, AriaTabPanelProps, mergeProps, Orientation, useFocusRing, useHover, useTab, useTabList, useTabPanel} from 'react-aria';
1515
import {Collection, CollectionBuilder, createHideableComponent, createLeafComponent} from '@react-aria/collections';
1616
import {CollectionProps, CollectionRendererContext, usePersistedKeys} from './Collection';
@@ -43,7 +43,7 @@ export interface TabListRenderProps {
4343
state: TabListState<unknown>
4444
}
4545

46-
export interface TabProps extends RenderProps<TabRenderProps>, AriaLabelingProps, LinkDOMProps {
46+
export interface TabProps extends RenderProps<TabRenderProps>, AriaLabelingProps, LinkDOMProps, HoverEvents {
4747
/** The unique id of the tab. */
4848
id?: Key,
4949
/** Whether the tab is disabled. */
@@ -252,7 +252,10 @@ export const Tab = /*#__PURE__*/ createLeafComponent('item', (props: TabProps, f
252252
let {tabProps, isSelected, isDisabled, isPressed} = useTab({key: item.key, ...props}, state, ref);
253253
let {focusProps, isFocused, isFocusVisible} = useFocusRing();
254254
let {hoverProps, isHovered} = useHover({
255-
isDisabled
255+
isDisabled,
256+
onHoverStart: props.onHoverStart,
257+
onHoverEnd: props.onHoverEnd,
258+
onHoverChange: props.onHoverChange
256259
});
257260

258261
let renderProps = useRenderProps({

packages/react-aria-components/test/Tabs.test.js

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,10 @@ describe('Tabs', () => {
116116
});
117117

118118
it('should support hover', async () => {
119-
let {getAllByRole} = renderTabs({}, {}, {className: ({isHovered}) => isHovered ? 'hover' : ''});
119+
let onHoverStart = jest.fn();
120+
let onHoverChange = jest.fn();
121+
let onHoverEnd = jest.fn();
122+
let {getAllByRole} = renderTabs({}, {}, {className: ({isHovered}) => isHovered ? 'hover' : '', onHoverStart, onHoverChange, onHoverEnd});
120123
let tab = getAllByRole('tab')[0];
121124

122125
expect(tab).not.toHaveAttribute('data-hovered');
@@ -125,10 +128,35 @@ describe('Tabs', () => {
125128
await user.hover(tab);
126129
expect(tab).toHaveAttribute('data-hovered', 'true');
127130
expect(tab).toHaveClass('hover');
131+
expect(onHoverStart).toHaveBeenCalledTimes(1);
132+
expect(onHoverChange).toHaveBeenCalledTimes(1);
128133

129134
await user.unhover(tab);
130135
expect(tab).not.toHaveAttribute('data-hovered');
131136
expect(tab).not.toHaveClass('hover');
137+
expect(onHoverEnd).toHaveBeenCalledTimes(1);
138+
expect(onHoverChange).toHaveBeenCalledTimes(2);
139+
});
140+
141+
it('should not show hover state when item is not interactive', async () => {
142+
let onHoverStart = jest.fn();
143+
let onHoverChange = jest.fn();
144+
let onHoverEnd = jest.fn();
145+
let {getAllByRole} = renderTabs({disabledKeys: ['a', 'b', 'c']}, {}, {className: ({isHovered}) => isHovered ? 'hover' : '', onHoverStart, onHoverChange, onHoverEnd});
146+
let tab = getAllByRole('tab')[0];
147+
148+
expect(tab).not.toHaveAttribute('data-hovered');
149+
expect(tab).not.toHaveClass('hover');
150+
expect(onHoverStart).not.toHaveBeenCalled();
151+
expect(onHoverChange).not.toHaveBeenCalled();
152+
expect(onHoverEnd).not.toHaveBeenCalled();
153+
154+
await user.hover(tab);
155+
expect(tab).not.toHaveAttribute('data-hovered');
156+
expect(tab).not.toHaveClass('hover');
157+
expect(onHoverStart).not.toHaveBeenCalled();
158+
expect(onHoverChange).not.toHaveBeenCalled();
159+
expect(onHoverEnd).not.toHaveBeenCalled();
132160
});
133161

134162
it('should support focus ring', async () => {

0 commit comments

Comments
 (0)