Add user status to autocomplete suggestion (#34241)

* Add user status to autocomplete suggestion

* Add test for PillCompletion titleIcon

* Convert to vitest

* Add test for UserStatusIcon

* Switch the user status icon view to be a view model based component

* Screenshots

* Add .catch

* Add test for UserProvider
This commit is contained in:
David Baker
2026-07-14 13:47:24 +00:00
committed by GitHub
parent 56bdadca12
commit c09f572fa7
12 changed files with 369 additions and 1 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

+1
View File
@@ -95,5 +95,6 @@ export * from "./core/utils/linkify";
export type * from "./core/userStatus.ts";
export * from "./status/SetStatusView";
export * from "./status/StatusTextView";
export * from "./status/UserStatusIconView";
// MVVM
export * from "./core/viewmodel";
@@ -0,0 +1,31 @@
/*
* 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 { type Meta, type StoryObj } from "@storybook/react-vite";
import { UserStatusIconView, type UserStatusIconViewModel } from "./UserStatusIconView";
import { MockViewModel } from "../core/viewmodel/MockViewModel";
const meta = {
title: "Status/UserStatusIconView",
component: UserStatusIconView,
tags: ["autodocs"],
args: {
vm: new MockViewModel({ status: { emoji: "🐎", text: "on a horse" } }) as UserStatusIconViewModel,
},
} satisfies Meta<typeof UserStatusIconView>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {};
export const NoStatus: Story = {
args: {
vm: new MockViewModel({ status: undefined }) as UserStatusIconViewModel,
},
};
@@ -0,0 +1,37 @@
/*
* 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 { composeStories } from "@storybook/react-vite";
import React from "react";
import { describe, expect, it } from "vitest";
import userEvent from "@testing-library/user-event";
import { render, screen, waitFor } from "@test-utils";
import * as stories from "./UserStatusIconView.stories";
const { Default, NoStatus } = composeStories(stories);
describe("UserStatusIconView", () => {
it("renders nothing when the user has no status", () => {
const { container } = render(<NoStatus />);
expect(container).toBeEmptyDOMElement();
});
it("renders the status emoji", () => {
render(<Default />);
expect(screen.getByText("🐎")).toBeInTheDocument();
});
it("shows the status text in a tooltip on hover", async () => {
render(<Default />);
await userEvent.hover(screen.getByText("🐎"));
await waitFor(() => {
expect(screen.getByRole("tooltip")).toHaveTextContent("on a horse");
});
});
});
@@ -0,0 +1,48 @@
/*
* 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, { type JSX } from "react";
import { Text, Tooltip } from "@vector-im/compound-web";
import { type ViewModel, useViewModel } from "../core/viewmodel";
import { type UserStatus } from "..";
/**
* Snapshot for the UserStatusIconView.
*/
export interface UserStatusIconViewSnapshot {
/**
* The user's status, or undefined if not available.
*/
status?: UserStatus;
}
/**
* The view model for UserStatusIconView.
*/
export type UserStatusIconViewModel = ViewModel<UserStatusIconViewSnapshot>;
interface UserStatusIconViewProps {
/**
* The view model for the user status icon.
*/
vm: UserStatusIconViewModel;
}
/**
* Displays the MSC4426 status emoji for a user, e.g. after their display name
* in the user mention autocomplete. Renders nothing if the user has no status.
*/
export function UserStatusIconView({ vm }: Readonly<UserStatusIconViewProps>): JSX.Element | null {
const { status } = useViewModel(vm);
if (!status) return null;
return (
<Tooltip description={status.text}>
<Text as="span">{status.emoji}</Text>
</Tooltip>
);
}