Migrate the room list view to shared components (#31921)
* Add NotificationDecoration component Add the NotificationDecoration component to shared-components. This is a leaf component that renders notification badges and indicators for rooms/items including mentions, unread counts, call indicators, etc. * Add RoomListItem component Add the RoomListItem component to shared-components. Includes context menu, hover menu, notification menu, and more options menu. * Add RoomListPrimaryFilters component Add filter chips component for filtering the room list by unread, people, rooms, favourites, mentions, invites, and low priority. * Update VirtualizedList component Update VirtualizedList to support the room list virtualization requirements. * Add RoomList component Add RoomList component that renders a virtualized list of room items. Includes story mocks for testing. * Add RoomListView component Add RoomListView component that composes RoomList with filters, empty states, and loading skeleton. * Export room-list components from shared-components Add exports for RoomListView, RoomListItem, RoomListPrimaryFilters, and RoomList. Include i18n strings for room list components. * Add RoomListItemViewModel Add view model for individual room list items. Manages per-room subscriptions and updates only when specific room data changes. * Add RoomListViewViewModel Add view model for the room list view. Manages room list state, filtering, keyboard navigation, and child view models. * Integrate shared components into RoomListView Update RoomListView to use the new ViewModels and shared components. Includes i18n string updates for element-web. * Remove old room list implementation Remove old ViewModels, hooks, and view components that are now replaced by the shared-components implementation. * Update sliding-sync playwright test Update test expectations for new room list implementation. * Add figma links * Move viewModels to the right folder * Rename to RoomListEmptyStateView * Update VirtualizedRoomListView naming * Update screenshots and snapshots * Move viewmodel tests to the right location and fix some imports * lint * Use unknown as an Opaque type rather than any. It discourages property access within shared components and can still be cast back in EW. * Update screenshots for new shared component rendering params * Make room order tests deterministic
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
/*
|
||||
Copyright 2025 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
* Copyright 2026 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React, { useRef, type JSX, useCallback, useEffect, useState, useMemo } from "react";
|
||||
import { type VirtuosoHandle, type ListRange, Virtuoso, type VirtuosoProps } from "react-virtuoso";
|
||||
@@ -95,6 +95,19 @@ export interface IVirtualizedListProps<Item, Context> extends Omit<
|
||||
* @returns
|
||||
*/
|
||||
onKeyDown?: (e: React.KeyboardEvent<HTMLDivElement>) => void;
|
||||
|
||||
/**
|
||||
* Optional total count of items (for virtualization with partial data loading).
|
||||
* If provided, this will be used instead of items.length for the total count.
|
||||
*/
|
||||
totalCount?: number;
|
||||
|
||||
/**
|
||||
* Optional callback when the visible range of items changes.
|
||||
* Useful for loading data on-demand as the user scrolls.
|
||||
* @param range - The new visible range with startIndex and endIndex
|
||||
*/
|
||||
rangeChanged?: (range: ListRange) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,7 +126,17 @@ export type ScrollIntoViewOnChange<Item, Context = any> = NonNullable<
|
||||
*/
|
||||
export function VirtualizedList<Item, Context = any>(props: IVirtualizedListProps<Item, Context>): React.ReactElement {
|
||||
// Extract our custom props to avoid conflicts with Virtuoso props
|
||||
const { items, getItemComponent, isItemFocusable, getItemKey, context, onKeyDown, ...virtuosoProps } = props;
|
||||
const {
|
||||
items,
|
||||
getItemComponent,
|
||||
isItemFocusable,
|
||||
getItemKey,
|
||||
context,
|
||||
onKeyDown,
|
||||
totalCount,
|
||||
rangeChanged,
|
||||
...virtuosoProps
|
||||
} = props;
|
||||
/** Reference to the Virtuoso component for programmatic scrolling */
|
||||
const virtuosoHandleRef = useRef<VirtuosoHandle>(null);
|
||||
/** Reference to the DOM element containing the virtualized list */
|
||||
@@ -324,6 +347,15 @@ export function VirtualizedList<Item, Context = any>(props: IVirtualizedListProp
|
||||
[tabIndexKey, isFocused, props.context],
|
||||
);
|
||||
|
||||
// Combine internal range tracking with optional external callback
|
||||
const handleRangeChanged = useCallback(
|
||||
(range: ListRange) => {
|
||||
setVisibleRange(range);
|
||||
rangeChanged?.(range);
|
||||
},
|
||||
[rangeChanged],
|
||||
);
|
||||
|
||||
return (
|
||||
<Virtuoso
|
||||
// note that either the container of direct children must be focusable to be axe
|
||||
@@ -334,10 +366,11 @@ export function VirtualizedList<Item, Context = any>(props: IVirtualizedListProp
|
||||
scrollerRef={scrollerRef}
|
||||
onKeyDown={keyDownCallback}
|
||||
context={listContext}
|
||||
rangeChanged={setVisibleRange}
|
||||
rangeChanged={handleRangeChanged}
|
||||
// virtuoso errors internally if you pass undefined.
|
||||
overscan={props.overscan || 0}
|
||||
data={props.items}
|
||||
totalCount={totalCount}
|
||||
onFocus={onFocus}
|
||||
onBlur={onBlur}
|
||||
itemContent={getItemComponentInternal}
|
||||
|
||||
Reference in New Issue
Block a user