Refactor ReactionsRowButtonTooltip to shared-components (#31866)

* Setting up structure for the init refactoring of ReactionsRowButtonTooltip

* implemented example to follow for refactoring to MVVM

* Refactoring of ReactionsRowButtonTooltipView

* updated reactionrowbutton to use our new viewmodel and removed unessecery comments

* Updated children from reactnode to propswithchildren

* removal of children on the vm have it as a props

* implemented constructor into reactionrowbutton to use vm to viewmodel

* Removal of old component

* Added ViewModel Tests for new viewmodel

* Fix issues after merging develop

* Updated import placement for eslint failure CI

* Add tests for ReactionsRowButton ViewModel integration and click handlers to pass coverage

* Added more tests to cover all conditions

* Pass MatrixClient as prop instead of using global; replace expect(true).toBe(true) with not.toThrow()

* Added new snapshot to reflect modifications on tests

* Update images to fit the CI tests

* Optimize reactions tooltip viewmodel updates

* Removal of module.css for reactionbuttontooltip, we dont need it since we dont use any css

* Fixed snapshots to show the tooltip by introducing a boolean to set open to true in Storybook.

* Update snapshots

---------

Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
Zack
2026-01-30 11:53:57 +00:00
committed by GitHub
co-authored by Michael Telatynski
parent 8cef5df140
commit 62c7fe5408
15 changed files with 941 additions and 73 deletions
+1
View File
@@ -15,6 +15,7 @@ export * from "./composer/Banner";
export * from "./crypto/SasEmoji";
export * from "./event-tiles/TextualEventView";
export * from "./message-body/MediaBody";
export * from "./message-body/ReactionsRowButtonTooltip";
export * from "./pill-input/Pill";
export * from "./pill-input/PillInput";
export * from "./room/RoomStatusBar";
@@ -0,0 +1,69 @@
/*
* 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, type PropsWithChildren } from "react";
import type { Meta, StoryFn } from "@storybook/react-vite";
import { useMockedViewModel } from "../../viewmodel";
import {
ReactionsRowButtonTooltipView,
type ReactionsRowButtonTooltipViewSnapshot,
} from "./ReactionsRowButtonTooltipView";
type WrapperProps = ReactionsRowButtonTooltipViewSnapshot & PropsWithChildren;
const ReactionsRowButtonTooltipViewWrapper = ({ children, ...snapshotProps }: WrapperProps): JSX.Element => {
const vm = useMockedViewModel(snapshotProps, {});
return <ReactionsRowButtonTooltipView vm={vm}>{children}</ReactionsRowButtonTooltipView>;
};
export default {
title: "MessageBody/ReactionsRowButtonTooltip",
component: ReactionsRowButtonTooltipViewWrapper,
tags: ["autodocs"],
argTypes: {
formattedSenders: { control: "text" },
caption: { control: "text" },
},
args: {
children: <button>👍 3</button>,
},
} as Meta<typeof ReactionsRowButtonTooltipViewWrapper>;
const Template: StoryFn<typeof ReactionsRowButtonTooltipViewWrapper> = (args) => (
<ReactionsRowButtonTooltipViewWrapper {...args} />
);
export const Default = Template.bind({});
Default.args = {
formattedSenders: "Alice, Bob and Charlie",
caption: ":thumbsup:",
tooltipOpen: true,
};
export const ManySenders = Template.bind({});
ManySenders.args = {
formattedSenders: "Alice, Bob, Charlie, David, Eve, Frank and 2 others",
caption: ":heart:",
children: <button> 8</button>,
tooltipOpen: true,
};
export const WithoutCaption = Template.bind({});
WithoutCaption.args = {
formattedSenders: "Alice and Bob",
caption: undefined,
children: <button>🎉 2</button>,
tooltipOpen: true,
};
export const NoTooltip = Template.bind({});
NoTooltip.args = {
formattedSenders: undefined,
caption: undefined,
children: <button>👍 1</button>,
};
@@ -0,0 +1,27 @@
/*
* 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 { render } from "@test-utils";
import React from "react";
import { describe, it, expect } from "vitest";
import * as stories from "./ReactionsRowButtonTooltip.stories";
const { Default, ManySenders } = composeStories(stories);
describe("ReactionsRowButtonTooltip", () => {
it("renders the tooltip with formatted senders and caption", () => {
const { container } = render(<Default />);
expect(container).toMatchSnapshot();
});
it("renders the tooltip with many senders", () => {
const { container } = render(<ManySenders />);
expect(container).toMatchSnapshot();
});
});
@@ -0,0 +1,63 @@
/*
* 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 PropsWithChildren, type JSX } from "react";
import React from "react";
import { Tooltip } from "@vector-im/compound-web";
import { type ViewModel, useViewModel } from "../../viewmodel";
/**
* Snapshot interface for the ReactionsRowButtonTooltip view.
*/
export interface ReactionsRowButtonTooltipViewSnapshot {
/**
* The formatted list of sender names who reacted.
*/
formattedSenders?: string;
/**
* The caption to display (e.g., the shortcode of the reaction).
*/
caption?: string;
/**
* Whether the tooltip should be forced open.
*/
tooltipOpen?: boolean;
}
export type ReactionsRowButtonTooltipViewModel = ViewModel<ReactionsRowButtonTooltipViewSnapshot>;
interface ReactionsRowButtonTooltipViewProps {
/**
* The view model for the reactions row button tooltip.
*/
vm: ReactionsRowButtonTooltipViewModel;
/**
* The children to wrap with the tooltip.
*/
children?: PropsWithChildren["children"];
}
/**
* Type alias for the ReactionsRowButtonTooltip view model.
*/
export function ReactionsRowButtonTooltipView({
vm,
children,
}: Readonly<ReactionsRowButtonTooltipViewProps>): JSX.Element {
const { formattedSenders, caption, tooltipOpen } = useViewModel(vm);
if (formattedSenders) {
return (
<Tooltip description={formattedSenders} caption={caption} placement="right" open={tooltipOpen}>
{children}
</Tooltip>
);
}
return <>{children}</>;
}
@@ -0,0 +1,21 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`ReactionsRowButtonTooltip > renders the tooltip with formatted senders and caption 1`] = `
<div>
<button
aria-describedby="_r_2_"
>
👍 3
</button>
</div>
`;
exports[`ReactionsRowButtonTooltip > renders the tooltip with many senders 1`] = `
<div>
<button
aria-describedby="_r_8_"
>
❤️ 8
</button>
</div>
`;
@@ -0,0 +1,12 @@
/*
* 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.
*/
export {
ReactionsRowButtonTooltipView,
type ReactionsRowButtonTooltipViewSnapshot,
type ReactionsRowButtonTooltipViewModel,
} from "./ReactionsRowButtonTooltipView";