Fix: Clicking on an item in the member list causes it to scroll to the top rather than show the profile view (#30455)
* Fix issue and add test * Fix MemberTileView * Add e2e test and comment
This commit is contained in:
@@ -42,7 +42,12 @@ export interface IListViewProps<Item, Context>
|
||||
* @param context - The context object containing the focused key and any additional data
|
||||
* @returns JSX element representing the rendered item
|
||||
*/
|
||||
getItemComponent: (index: number, item: Item, context: ListContext<Context>) => JSX.Element;
|
||||
getItemComponent: (
|
||||
index: number,
|
||||
item: Item,
|
||||
context: ListContext<Context>,
|
||||
onFocus: (e: React.FocusEvent) => void,
|
||||
) => JSX.Element;
|
||||
|
||||
/**
|
||||
* Optional additional context data to pass to each rendered item.
|
||||
@@ -217,6 +222,20 @@ export function ListView<Item, Context = any>(props: IListViewProps<Item, Contex
|
||||
virtuosoDomRef.current = element;
|
||||
}, []);
|
||||
|
||||
const getItemComponentInternal = useCallback(
|
||||
(index: number, item: Item, context: ListContext<Context>): JSX.Element => {
|
||||
const onFocus = (e: React.FocusEvent): void => {
|
||||
// If one of the item components has been focused directly, set the focused and tabIndex state
|
||||
// and stop propagation so the ListViews onFocus doesn't also handle it.
|
||||
const key = getItemKey(item);
|
||||
setIsFocused(true);
|
||||
setTabIndexKey(key);
|
||||
e.stopPropagation();
|
||||
};
|
||||
return getItemComponent(index, item, context, onFocus);
|
||||
},
|
||||
[getItemComponent, getItemKey],
|
||||
);
|
||||
/**
|
||||
* Handles focus events on the list.
|
||||
* Sets the focused state and scrolls to the focused item if it is not currently visible.
|
||||
@@ -265,7 +284,7 @@ export function ListView<Item, Context = any>(props: IListViewProps<Item, Contex
|
||||
data={props.items}
|
||||
onFocus={onFocus}
|
||||
onBlur={onBlur}
|
||||
itemContent={props.getItemComponent}
|
||||
itemContent={getItemComponentInternal}
|
||||
{...virtuosoProps}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -41,7 +41,12 @@ const MemberListView: React.FC<IProps> = (props: IProps) => {
|
||||
}, []);
|
||||
|
||||
const getItemComponent = useCallback(
|
||||
(index: number, item: MemberWithSeparator, context: ListContext<any>): JSX.Element => {
|
||||
(
|
||||
index: number,
|
||||
item: MemberWithSeparator,
|
||||
context: ListContext<any>,
|
||||
onFocus: (e: React.FocusEvent) => void,
|
||||
): JSX.Element => {
|
||||
const itemKey = getItemKey(item);
|
||||
const isRovingItem = itemKey === context.tabIndexKey;
|
||||
const focused = isRovingItem && context.focused;
|
||||
@@ -56,6 +61,7 @@ const MemberListView: React.FC<IProps> = (props: IProps) => {
|
||||
tabIndex={isRovingItem ? 0 : -1}
|
||||
index={index}
|
||||
memberCount={memberCount}
|
||||
onFocus={onFocus}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
@@ -66,6 +72,7 @@ const MemberListView: React.FC<IProps> = (props: IProps) => {
|
||||
tabIndex={isRovingItem ? 0 : -1}
|
||||
memberIndex={index - 1} // Adjust as invites are below the separator
|
||||
memberCount={memberCount}
|
||||
onFocus={onFocus}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ interface IProps {
|
||||
showPresence?: boolean;
|
||||
focused?: boolean;
|
||||
tabIndex?: number;
|
||||
onFocus: (e: React.FocusEvent) => void;
|
||||
}
|
||||
|
||||
export function RoomMemberTileView(props: IProps): JSX.Element {
|
||||
@@ -59,6 +60,7 @@ export function RoomMemberTileView(props: IProps): JSX.Element {
|
||||
return (
|
||||
<MemberTileView
|
||||
onClick={vm.onClick}
|
||||
onFocus={props.onFocus}
|
||||
avatarJsx={av}
|
||||
presenceJsx={presenceJSX}
|
||||
nameJsx={nameJSX}
|
||||
|
||||
@@ -19,6 +19,7 @@ interface Props {
|
||||
memberCount: number;
|
||||
focused?: boolean;
|
||||
tabIndex?: number;
|
||||
onFocus: (e: React.FocusEvent) => void;
|
||||
}
|
||||
|
||||
export function ThreePidInviteTileView(props: Props): JSX.Element {
|
||||
@@ -39,6 +40,7 @@ export function ThreePidInviteTileView(props: Props): JSX.Element {
|
||||
iconJsx={iconJsx}
|
||||
focused={props.focused}
|
||||
tabIndex={props.tabIndex}
|
||||
onFocus={props.onFocus}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ interface Props {
|
||||
avatarJsx: JSX.Element;
|
||||
nameJsx: JSX.Element | string;
|
||||
onClick: () => void;
|
||||
onFocus: (e: React.FocusEvent) => void;
|
||||
memberIndex: number;
|
||||
memberCount: number;
|
||||
ariaLabel?: string;
|
||||
@@ -41,6 +42,7 @@ export function MemberTileView(props: Props): JSX.Element {
|
||||
ref={ref}
|
||||
className="mx_MemberTileView"
|
||||
onClick={props.onClick}
|
||||
onFocus={props.onFocus}
|
||||
aria-label={props?.ariaLabel}
|
||||
tabIndex={props.tabIndex}
|
||||
role="option"
|
||||
|
||||
Reference in New Issue
Block a user