Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "bugfix: ensure TreeItem emits events properly",
"packageName": "@fluentui/react-tree",
"email": "bernardo.sunderhus@gmail.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,14 @@ export type TreeOpenChangeData = {
} | {
event: React_2.MouseEvent<HTMLElement>;
type: 'Click';
} | {
}
/**
* @deprecated
* Use `type: 'Click'` instead of Enter,
* an enter press will trigger a click event, which will trigger an open change,
* so there is no need to have a separate type for it.
*/
| {
event: React_2.KeyboardEvent<HTMLElement>;
type: typeof Enter;
} | {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ export type TreeOpenChangeData = {
} & (
| { event: React.MouseEvent<HTMLElement>; type: 'ExpandIconClick' }
| { event: React.MouseEvent<HTMLElement>; type: 'Click' }
/**
* @deprecated
* Use `type: 'Click'` instead of Enter,
* an enter press will trigger a click event, which will trigger an open change,
* so there is no need to have a separate type for it.
*/
| { event: React.KeyboardEvent<HTMLElement>; type: typeof Enter }
| { event: React.KeyboardEvent<HTMLElement>; type: typeof ArrowRight }
| { event: React.KeyboardEvent<HTMLElement>; type: typeof ArrowLeft }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,19 @@ export function useTreeItem_unstable(props: TreeItemProps, ref: React.Ref<HTMLDi
// then selection and expansion will not work properly
const value = useId('fuiTreeItemValue-', props.value?.toString());

const { onClick, onKeyDown, as = 'div', itemType = 'leaf', 'aria-level': level = contextLevel, ...rest } = props;
const {
onClick,
onKeyDown,
onMouseOver,
onFocus,
onMouseOut,
onBlur,
onChange,
as = 'div',
itemType = 'leaf',
'aria-level': level = contextLevel,
...rest
} = props;

const [isActionsVisible, setActionsVisible] = React.useState(false);
const [isAsideVisible, setAsideVisible] = React.useState(true);
Expand Down Expand Up @@ -102,23 +114,12 @@ export function useTreeItem_unstable(props: TreeItemProps, ref: React.Ref<HTMLDi
case Space:
if (selectionMode !== 'none') {
selectionRef.current?.click();
// Prevents the page from scrolling down when the spacebar is pressed
event.preventDefault();
}
return;
case treeDataTypes.Enter: {
const data = {
value,
event,
open: !open,
type: event.key,
target: event.currentTarget,
} as const;
props.onOpenChange?.(event, data);
return requestTreeResponse({
...data,
itemType,
requestType: 'open',
});
return event.currentTarget.click();
}
case treeDataTypes.End:
case treeDataTypes.Home:
Expand Down Expand Up @@ -192,31 +193,53 @@ export function useTreeItem_unstable(props: TreeItemProps, ref: React.Ref<HTMLDi
}
});

const handleActionsVisible = useEventCallback((event: React.FocusEvent | React.MouseEvent) => {
const setActionsVisibleIfNotFromSubtree = React.useCallback((event: React.SyntheticEvent<HTMLDivElement>) => {
const isTargetFromSubtree = Boolean(
subtreeRef.current && elementContains(subtreeRef.current, event.target as Node),
);
if (!isTargetFromSubtree) {
setActionsVisible(true);
}
}, []);
const setActionsInvisibleIfNotFromSubtree = React.useCallback(
(event: React.MouseEvent<HTMLDivElement> | React.FocusEvent<HTMLDivElement>) => {
const isTargetFromSubtree = Boolean(
subtreeRef.current && elementContains(subtreeRef.current, event.target as Node),
);
const isRelatedTargetFromActions = Boolean(
actionsRef.current && elementContains(actionsRef.current, event.relatedTarget as Node),
);
if (isRelatedTargetFromActions) {
return setActionsVisible(true);
}
if (!isTargetFromSubtree) {
return setActionsVisible(false);
}
},
[],
);

const handleMouseOver = useEventCallback((event: React.MouseEvent<HTMLDivElement>) => {
onMouseOver?.(event);
setActionsVisibleIfNotFromSubtree(event);
});

const handleActionsInvisible = useEventCallback((event: React.FocusEvent | React.MouseEvent) => {
const isTargetFromSubtree = Boolean(
subtreeRef.current && elementContains(subtreeRef.current, event.target as Node),
);
const isRelatedTargetFromActions = Boolean(
actionsRef.current && elementContains(actionsRef.current, event.relatedTarget as Node),
);
if (isRelatedTargetFromActions) {
return setActionsVisible(true);
}
if (!isTargetFromSubtree) {
return setActionsVisible(false);
}
const handleFocus = useEventCallback((event: React.FocusEvent<HTMLDivElement>) => {
onFocus?.(event);
setActionsVisibleIfNotFromSubtree(event);
});

const handleMouseOut = useEventCallback((event: React.MouseEvent<HTMLDivElement>) => {
onMouseOut?.(event);
setActionsInvisibleIfNotFromSubtree(event);
});
const handleBlur = useEventCallback((event: React.FocusEvent<HTMLDivElement>) => {
onBlur?.(event);
setActionsInvisibleIfNotFromSubtree(event);
});

const handleChange = useEventCallback((event: React.ChangeEvent<HTMLInputElement>) => {
onChange?.(event);
if (event.isDefaultPrevented()) {
return;
}
Expand Down Expand Up @@ -267,10 +290,10 @@ export function useTreeItem_unstable(props: TreeItemProps, ref: React.Ref<HTMLDi
'aria-expanded': itemType === 'branch' ? open : undefined,
onClick: handleClick,
onKeyDown: handleKeyDown,
onMouseOver: handleActionsVisible,
onFocus: handleActionsVisible,
onMouseOut: handleActionsInvisible,
onBlur: handleActionsInvisible,
onMouseOver: handleMouseOver,
onFocus: handleFocus,
onMouseOut: handleMouseOut,
onBlur: handleBlur,
onChange: handleChange,
}),
{ elementType: 'div' },
Expand Down