From 83d732d60efbbc076cee765ce0043022c65caa17 Mon Sep 17 00:00:00 2001 From: rbondesson Date: Thu, 5 Mar 2026 09:36:45 +0100 Subject: [PATCH] Refactor className and children to component properties instead och view model snapshots in shared components (#32711) * Refactor className? to component property in EncryptionEventView * Refactor extraClassNames to default react className as component property for DecryptionFailureBodyView * Refactor className to component property for MessageTimestampView * Refactor className and children to component properties for ReactionsRowButton * Refactor className to component property for DisambiguatedProfile * Refactor className to a component property in DateSeparatorView * Fix for lint errors and EncryptionEventView unsupported icon color * EncryptionEventView fix for icon color css specificity/order --- .../views/messages/_common_CryptoEvent.pcss | 24 +---------- .../components/structures/MessagePanel.tsx | 2 +- .../src/components/structures/RoomView.tsx | 3 +- .../WaitingForThirdPartyRoomView.tsx | 2 +- .../structures/grouper/CreationGrouper.tsx | 2 +- .../structures/grouper/MainGrouper.tsx | 2 +- .../dialogs/MessageEditHistoryDialog.tsx | 5 ++- .../components/views/elements/ImageView.tsx | 2 +- .../messages/MKeyVerificationRequest.tsx | 2 +- .../views/messages/MessageEvent.tsx | 2 +- .../views/messages/SenderProfile.tsx | 3 +- .../src/components/views/rooms/EventTile.tsx | 12 +++--- .../MemberList/tiles/RoomMemberTileView.tsx | 3 +- .../views/rooms/SearchResultTile.tsx | 2 +- apps/web/src/events/EventTileFactory.tsx | 3 +- apps/web/src/utils/exportUtils/HtmlExport.tsx | 2 +- .../event-tiles/EncryptionEventViewModel.ts | 4 -- .../DecryptionFailureBodyViewModel.ts | 15 +------ .../message-body/MessageTimestampViewModel.ts | 2 - .../message-body/ReactionsRowViewModel.ts | 19 +-------- .../profile/DisambiguatedProfileViewModel.ts | 7 +-- .../timeline/DateSeparatorViewModel.tsx | 2 - .../__snapshots__/RoomView-test.tsx.snap | 3 +- .../EncryptionEventViewModel-test.ts | 2 - .../DecryptionFailureBodyViewModel-test.tsx | 13 ------ .../MessageTimestampViewModel-test.tsx | 3 +- .../ReactionsRowViewModel-test.tsx | 1 - .../DisambiguatedProfileViewModel-test.tsx | 12 ------ .../timeline/DateSeparatorViewModel-test.tsx | 1 - .../has-extra-class-names-auto.png | Bin 20715 -> 0 bytes .../has-extra-class-names-auto.png | Bin 17284 -> 0 bytes .../add-reaction-button-active-auto.png | Bin ...eaction-button-hidden-until-hover-auto.png | Bin .../ReactionsRow.stories.tsx/default-auto.png | Bin .../ReactionsRow.stories.tsx/hidden-auto.png | Bin .../with-show-all-button-auto.png | Bin .../EncryptionEventView.module.css | 6 ++- .../EncryptionEventView.stories.tsx | 7 ++- .../EncryptionEventView.test.tsx | 3 +- .../EncryptionEventView.tsx | 40 ++++++++++++++---- .../EncryptionEventView.test.tsx.snap | 25 +++++++---- packages/shared-components/src/index.ts | 2 +- .../DecryptionFailureBodyView.stories.tsx | 24 +++++------ .../DecryptionFailureBodyView.test.tsx | 21 +++++---- .../DecryptionFailureBodyView.tsx | 32 ++++++++++---- .../DecryptionFailureBodyView.test.tsx.snap | 10 ----- .../MessageTimestampView.stories.tsx | 15 +++---- .../MessageTimestampView.test.tsx | 15 +++++-- .../MessageTimestampView.tsx | 20 +++++---- .../MessageTimestampView.test.tsx.snap | 12 ------ .../ReactionsRow.module.css | 0 .../ReactionsRow.stories.tsx | 12 ++++-- .../ReactionsRow.test.tsx | 19 ++++++++- .../ReactionsRowView.tsx | 22 +++++----- .../__snapshots__/ReactionsRow.test.tsx.snap | 0 .../{ReactionRow => ReactionsRow}/index.tsx | 0 .../DisambiguatedProfile.stories.tsx | 8 +++- .../DisambiguatedProfile.test.tsx | 10 +++++ .../DisambiguatedProfileView.tsx | 12 +++--- .../DateSeparatorView.stories.tsx | 5 ++- .../DateSeparatorView/DateSeparatorView.tsx | 12 +++--- 61 files changed, 232 insertions(+), 255 deletions(-) delete mode 100644 packages/shared-components/__vis__/linux/__baselines__/message-body/DecryptionFailureBodyView/DecryptionFailureBodyView.stories.tsx/has-extra-class-names-auto.png delete mode 100644 packages/shared-components/__vis__/linux/__baselines__/message-body/MessageTimestampView/MessageTimestampView.stories.tsx/has-extra-class-names-auto.png rename packages/shared-components/__vis__/linux/__baselines__/message-body/{ReactionRow => ReactionsRow}/ReactionsRow.stories.tsx/add-reaction-button-active-auto.png (100%) rename packages/shared-components/__vis__/linux/__baselines__/message-body/{ReactionRow => ReactionsRow}/ReactionsRow.stories.tsx/add-reaction-button-hidden-until-hover-auto.png (100%) rename packages/shared-components/__vis__/linux/__baselines__/message-body/{ReactionRow => ReactionsRow}/ReactionsRow.stories.tsx/default-auto.png (100%) rename packages/shared-components/__vis__/linux/__baselines__/message-body/{ReactionRow => ReactionsRow}/ReactionsRow.stories.tsx/hidden-auto.png (100%) rename packages/shared-components/__vis__/linux/__baselines__/message-body/{ReactionRow => ReactionsRow}/ReactionsRow.stories.tsx/with-show-all-button-auto.png (100%) rename packages/shared-components/src/message-body/{ReactionRow => ReactionsRow}/ReactionsRow.module.css (100%) rename packages/shared-components/src/message-body/{ReactionRow => ReactionsRow}/ReactionsRow.stories.tsx (91%) rename packages/shared-components/src/message-body/{ReactionRow => ReactionsRow}/ReactionsRow.test.tsx (83%) rename packages/shared-components/src/message-body/{ReactionRow => ReactionsRow}/ReactionsRowView.tsx (94%) rename packages/shared-components/src/message-body/{ReactionRow => ReactionsRow}/__snapshots__/ReactionsRow.test.tsx.snap (100%) rename packages/shared-components/src/message-body/{ReactionRow => ReactionsRow}/index.tsx (100%) diff --git a/apps/web/res/css/views/messages/_common_CryptoEvent.pcss b/apps/web/res/css/views/messages/_common_CryptoEvent.pcss index affbd26d64..06b7ccc1c7 100644 --- a/apps/web/res/css/views/messages/_common_CryptoEvent.pcss +++ b/apps/web/res/css/views/messages/_common_CryptoEvent.pcss @@ -9,29 +9,7 @@ Please see LICENSE files in the repository root for full details. .mx_EventTileBubble.mx_cryptoEvent { margin: var(--EventTileBubble_margin-block) auto; - &.mx_cryptoEvent_icon svg { + svg[data-state="supported"] { color: $header-panel-text-primary-color; } - - .mx_cryptoEvent_state, - .mx_cryptoEvent_buttons { - grid-column: 3; - grid-row: 1 / 3; - } - - .mx_cryptoEvent_buttons { - align-items: center; - display: flex; - gap: 5px; - } - - .mx_cryptoEvent_state { - width: 130px; - padding: 10px 20px; - margin: auto 0; - text-align: center; - color: $tertiary-content; - overflow-wrap: break-word; - font-size: $font-12px; - } } diff --git a/apps/web/src/components/structures/MessagePanel.tsx b/apps/web/src/components/structures/MessagePanel.tsx index 207b91b0b6..2b0ebeacf2 100644 --- a/apps/web/src/components/structures/MessagePanel.tsx +++ b/apps/web/src/components/structures/MessagePanel.tsx @@ -66,7 +66,7 @@ const continuedTypes = [EventType.Sticker, EventType.RoomMessage]; */ function DateSeparatorWrapper({ roomId, ts }: { roomId: string; ts: number }): JSX.Element { const vm = useCreateAutoDisposedViewModel(() => new DateSeparatorViewModel({ roomId, ts })); - return ; + return ; } /** diff --git a/apps/web/src/components/structures/RoomView.tsx b/apps/web/src/components/structures/RoomView.tsx index a8050571b2..18375a9580 100644 --- a/apps/web/src/components/structures/RoomView.tsx +++ b/apps/web/src/components/structures/RoomView.tsx @@ -416,7 +416,8 @@ function RoomStatusBarWrappedView(props: ConstructorParameters new EncryptionEventViewModel({ mxEvent, cli })); - return ; + + return ; } export class RoomView extends React.Component { diff --git a/apps/web/src/components/structures/WaitingForThirdPartyRoomView.tsx b/apps/web/src/components/structures/WaitingForThirdPartyRoomView.tsx index 0b0e07bbeb..00228ed9a0 100644 --- a/apps/web/src/components/structures/WaitingForThirdPartyRoomView.tsx +++ b/apps/web/src/components/structures/WaitingForThirdPartyRoomView.tsx @@ -45,7 +45,7 @@ export const WaitingForThirdPartyRoomView: React.FC = ({ roomView, resize } - className="mx_EventTileBubble mx_cryptoEvent mx_cryptoEvent_icon" + className="mx_EventTileBubble mx_cryptoEvent" title={_t("room|waiting_for_join_title", { brand })} subtitle={_t("room|waiting_for_join_subtitle", { brand })} /> diff --git a/apps/web/src/components/structures/grouper/CreationGrouper.tsx b/apps/web/src/components/structures/grouper/CreationGrouper.tsx index eafd7ade1d..9608f18b0f 100644 --- a/apps/web/src/components/structures/grouper/CreationGrouper.tsx +++ b/apps/web/src/components/structures/grouper/CreationGrouper.tsx @@ -29,7 +29,7 @@ import { DateSeparatorViewModel } from "../../../viewmodels/timeline/DateSeparat */ function DateSeparatorWrapper({ roomId, ts }: { roomId: string; ts: number }): ReactNode { const vm = useCreateAutoDisposedViewModel(() => new DateSeparatorViewModel({ roomId, ts })); - return ; + return ; } export class CreationGrouper extends BaseGrouper { diff --git a/apps/web/src/components/structures/grouper/MainGrouper.tsx b/apps/web/src/components/structures/grouper/MainGrouper.tsx index bf9a178fc8..f7a0eb4d94 100644 --- a/apps/web/src/components/structures/grouper/MainGrouper.tsx +++ b/apps/web/src/components/structures/grouper/MainGrouper.tsx @@ -31,7 +31,7 @@ const groupedStateEvents = [ */ function DateSeparatorWrapper({ roomId, ts }: { roomId: string; ts: number }): ReactNode { const vm = useCreateAutoDisposedViewModel(() => new DateSeparatorViewModel({ roomId, ts })); - return ; + return ; } // Wrap consecutive grouped events in a ListSummary diff --git a/apps/web/src/components/views/dialogs/MessageEditHistoryDialog.tsx b/apps/web/src/components/views/dialogs/MessageEditHistoryDialog.tsx index 599d2c32f0..950c77c432 100644 --- a/apps/web/src/components/views/dialogs/MessageEditHistoryDialog.tsx +++ b/apps/web/src/components/views/dialogs/MessageEditHistoryDialog.tsx @@ -143,7 +143,10 @@ export default class MessageEditHistoryDialog extends React.PureComponent - + , ); } diff --git a/apps/web/src/components/views/elements/ImageView.tsx b/apps/web/src/components/views/elements/ImageView.tsx index 983cd42575..6c72c8b276 100644 --- a/apps/web/src/components/views/elements/ImageView.tsx +++ b/apps/web/src/components/views/elements/ImageView.tsx @@ -657,5 +657,5 @@ function MessageTimestampWrapper(props: MessageTimestampViewModelProps): JSX.Ele vm.setHref(props.href); vm.setHandlers({ onClick: props.onClick }); }, [vm, props]); - return ; + return ; } diff --git a/apps/web/src/components/views/messages/MKeyVerificationRequest.tsx b/apps/web/src/components/views/messages/MKeyVerificationRequest.tsx index 022766868e..6765f330da 100644 --- a/apps/web/src/components/views/messages/MKeyVerificationRequest.tsx +++ b/apps/web/src/components/views/messages/MKeyVerificationRequest.tsx @@ -75,7 +75,7 @@ const MKeyVerificationRequest: React.FC = ({ mxEvent, timestamp }) => { return ( } - className="mx_EventTileBubble mx_cryptoEvent mx_cryptoEvent_icon" + className="mx_EventTileBubble mx_cryptoEvent" title={title} subtitle={subtitle} > diff --git a/apps/web/src/components/views/messages/MessageEvent.tsx b/apps/web/src/components/views/messages/MessageEvent.tsx index 6bb28fd188..961e090d13 100644 --- a/apps/web/src/components/views/messages/MessageEvent.tsx +++ b/apps/web/src/components/views/messages/MessageEvent.tsx @@ -347,5 +347,5 @@ function DecryptionFailureBodyWrapper({ mxEvent, ref }: IBodyProps): JSX.Element useEffect(() => { vm.setVerificationState(verificationState); }, [verificationState, vm]); - return ; + return ; } diff --git a/apps/web/src/components/views/messages/SenderProfile.tsx b/apps/web/src/components/views/messages/SenderProfile.tsx index 89b483f735..a68083009f 100644 --- a/apps/web/src/components/views/messages/SenderProfile.tsx +++ b/apps/web/src/components/views/messages/SenderProfile.tsx @@ -37,7 +37,6 @@ export default function SenderProfile({ mxEvent, onClick, withTooltip }: IProps) colored: true, emphasizeDisplayName: true, withTooltip, - className: "mx_DisambiguatedProfile", }), ); @@ -45,7 +44,7 @@ export default function SenderProfile({ mxEvent, onClick, withTooltip }: IProps) disambiguatedProfileVM.setMember(sender ?? "", member); }, [disambiguatedProfileVM, member, sender]); return mxEvent.getContent().msgtype !== MsgType.Emote ? ( - + ) : ( <> ); diff --git a/apps/web/src/components/views/rooms/EventTile.tsx b/apps/web/src/components/views/rooms/EventTile.tsx index a6b7b7c326..af35f2c800 100644 --- a/apps/web/src/components/views/rooms/EventTile.tsx +++ b/apps/web/src/components/views/rooms/EventTile.tsx @@ -1617,7 +1617,7 @@ function DecryptionFailureBodyWrapper({ mxEvent }: { mxEvent: MatrixEvent }): JS vm.setVerificationState(verificationState); }, [verificationState, vm]); - return ; + return ; } /** @@ -1643,7 +1643,7 @@ function MessageTimestampWrapper(props: MessageTimestampViewModelProps): JSX.Ele {props.receivedTs ? ( ) : undefined} - + ); } @@ -1859,10 +1859,6 @@ function ReactionsRowWrapper({ mxEvent, reactions }: Readonly { - vm.setChildren(items); - }, [items, vm]); - if (!snapshot.isVisible || !items?.length) { return null; } @@ -1878,7 +1874,9 @@ function ReactionsRowWrapper({ mxEvent, reactions }: Readonly - + + {items} + {contextMenu} ); diff --git a/apps/web/src/components/views/rooms/MemberList/tiles/RoomMemberTileView.tsx b/apps/web/src/components/views/rooms/MemberList/tiles/RoomMemberTileView.tsx index f8dbd02829..e298943e3a 100644 --- a/apps/web/src/components/views/rooms/MemberList/tiles/RoomMemberTileView.tsx +++ b/apps/web/src/components/views/rooms/MemberList/tiles/RoomMemberTileView.tsx @@ -53,13 +53,12 @@ export function RoomMemberTileView(props: IProps): JSX.Element { fallbackName: name, member, withTooltip: true, - className: "mx_DisambiguatedProfile", }), ); useEffect(() => { disambiguatedProfileVM.setMember(name, member); }, [disambiguatedProfileVM, member, name]); - const nameJSX = ; + const nameJSX = ; const presenceState = member.presenceState; let presenceJSX: JSX.Element | undefined; diff --git a/apps/web/src/components/views/rooms/SearchResultTile.tsx b/apps/web/src/components/views/rooms/SearchResultTile.tsx index 1b45dbf024..8e4e7e4f3e 100644 --- a/apps/web/src/components/views/rooms/SearchResultTile.tsx +++ b/apps/web/src/components/views/rooms/SearchResultTile.tsx @@ -40,7 +40,7 @@ interface IProps { */ function DateSeparatorWrapper({ roomId, ts }: { roomId: string; ts: number }): JSX.Element { const vm = useCreateAutoDisposedViewModel(() => new DateSeparatorViewModel({ roomId, ts })); - return ; + return ; } export default class SearchResultTile extends React.Component { diff --git a/apps/web/src/events/EventTileFactory.tsx b/apps/web/src/events/EventTileFactory.tsx index 41286c5075..fb637fec30 100644 --- a/apps/web/src/events/EventTileFactory.tsx +++ b/apps/web/src/events/EventTileFactory.tsx @@ -88,7 +88,8 @@ export const TextualEventFactory: Factory = (ref, props) => { function EncryptionEventWrappedView({ mxEvent, ref }: IBodyProps): JSX.Element { const cli = useMatrixClientContext(); const vm = useCreateAutoDisposedViewModel(() => new EncryptionEventViewModel({ mxEvent, cli })); - return ; + + return ; } const EncryptionEventFactory: Factory = (ref, props) => { return ; diff --git a/apps/web/src/utils/exportUtils/HtmlExport.tsx b/apps/web/src/utils/exportUtils/HtmlExport.tsx index 4ae8e231d4..ce8f3fd14f 100644 --- a/apps/web/src/utils/exportUtils/HtmlExport.tsx +++ b/apps/web/src/utils/exportUtils/HtmlExport.tsx @@ -266,7 +266,7 @@ export default class HTMLExporter extends Exporter { try { const dateSeparator = (
  • - +
  • ); return this.renderToStaticMarkupWithProviders(dateSeparator); diff --git a/apps/web/src/viewmodels/event-tiles/EncryptionEventViewModel.ts b/apps/web/src/viewmodels/event-tiles/EncryptionEventViewModel.ts index 03a840b621..6084aef56b 100644 --- a/apps/web/src/viewmodels/event-tiles/EncryptionEventViewModel.ts +++ b/apps/web/src/viewmodels/event-tiles/EncryptionEventViewModel.ts @@ -78,13 +78,11 @@ export class EncryptionEventViewModel props: EncryptionEventViewModelProps, isEncrypted: boolean, ): EncryptionEventViewSnapshotInterface { - // Keep legacy class names for compatibility with existing timeline layout and styling. const newSnapshot: EncryptionEventViewSnapshotInterface = { state: EncryptionEventState.CHANGED, encryptedStateEvents: undefined, userName: undefined, timestamp: props.timestamp, - className: "mx_EventTileBubble mx_cryptoEvent mx_cryptoEvent_icon", }; const content = props.mxEvent.getContent(); @@ -113,8 +111,6 @@ export class EncryptionEventViewModel newSnapshot.state = EncryptionEventState.DISABLE_ATTEMPT; } else { newSnapshot.state = EncryptionEventState.UNSUPPORTED; - // Unsupported branch matches legacy EncryptionEvent class usage (no icon class). - newSnapshot.className = "mx_EventTileBubble mx_cryptoEvent"; } return newSnapshot; diff --git a/apps/web/src/viewmodels/message-body/DecryptionFailureBodyViewModel.ts b/apps/web/src/viewmodels/message-body/DecryptionFailureBodyViewModel.ts index 965c4a0942..a51ea71117 100644 --- a/apps/web/src/viewmodels/message-body/DecryptionFailureBodyViewModel.ts +++ b/apps/web/src/viewmodels/message-body/DecryptionFailureBodyViewModel.ts @@ -22,10 +22,6 @@ export interface DecryptionFailureBodyViewModelProps { * The local device verification state. */ verificationState?: boolean; - /** - * Extra CSS classes to apply to the component - */ - extraClassNames?: string[]; } /** @@ -62,30 +58,21 @@ export class DecryptionFailureBodyViewModel /** * @param decryptionFailureCode - The decryption failure code for the event. * @param verificationState - The local device verification state. - * @param extraClassNames - Extra CSS classes to apply to the component. */ private static readonly computeSnapshot = ( decryptionFailureCode: DecryptionFailureCode | null, verificationState?: boolean, - extraClassNames?: string[], ): DecryptionFailureBodyViewSnapshotInterface => { - // Keep mx_DecryptionFailureBody and mx_EventTile_content to support the compatibility with existing timeline and the all the layout - const defaultClassNames = ["mx_DecryptionFailureBody", "mx_EventTile_content"]; return { decryptionFailureReason: DecryptionFailureBodyViewModel.getDecryptionReasonFromCode(decryptionFailureCode), isLocalDeviceVerified: verificationState, - extraClassNames: extraClassNames ? defaultClassNames.concat(extraClassNames) : defaultClassNames, }; }; public constructor(props: DecryptionFailureBodyViewModelProps) { super( props, - DecryptionFailureBodyViewModel.computeSnapshot( - props.decryptionFailureCode, - props.verificationState, - props.extraClassNames, - ), + DecryptionFailureBodyViewModel.computeSnapshot(props.decryptionFailureCode, props.verificationState), ); } diff --git a/apps/web/src/viewmodels/message-body/MessageTimestampViewModel.ts b/apps/web/src/viewmodels/message-body/MessageTimestampViewModel.ts index 8d9299ba88..da4295176c 100644 --- a/apps/web/src/viewmodels/message-body/MessageTimestampViewModel.ts +++ b/apps/web/src/viewmodels/message-body/MessageTimestampViewModel.ts @@ -91,14 +91,12 @@ export class MessageTimestampViewModel receivedAt = formatFullDate(receivedDate, props.showTwelveHour); } - // Keep mx_MessageTimestamp for compatibility with the existing timeline and layout. return { ts: timestamp, tsSentAt: sentAt, tsReceivedAt: receivedAt, inhibitTooltip: props.inhibitTooltip, href: props.href, - className: "mx_MessageTimestamp", }; }; diff --git a/apps/web/src/viewmodels/message-body/ReactionsRowViewModel.ts b/apps/web/src/viewmodels/message-body/ReactionsRowViewModel.ts index 48443b2ea2..3842b25c36 100644 --- a/apps/web/src/viewmodels/message-body/ReactionsRowViewModel.ts +++ b/apps/web/src/viewmodels/message-body/ReactionsRowViewModel.ts @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -import { type MouseEvent, type MouseEventHandler, type ReactNode } from "react"; +import { type MouseEvent, type MouseEventHandler } from "react"; import { BaseViewModel, type ReactionsRowViewSnapshot, @@ -41,10 +41,6 @@ export interface ReactionsRowViewModelProps { * Optional callback invoked on add-reaction button context-menu. */ onAddReactionContextMenu?: MouseEventHandler; - /** - * Reaction row children (typically reaction buttons). - */ - children?: ReactNode; } interface InternalProps extends ReactionsRowViewModelProps { @@ -59,18 +55,16 @@ export class ReactionsRowViewModel props: InternalProps, ): Pick< ReactionsRowViewSnapshot, - "isVisible" | "showAllButtonVisible" | "showAddReactionButton" | "addReactionButtonActive" | "children" + "isVisible" | "showAllButtonVisible" | "showAddReactionButton" | "addReactionButtonActive" > => ({ isVisible: props.isActionable && props.reactionGroupCount > 0, showAllButtonVisible: props.reactionGroupCount > MAX_ITEMS_WHEN_LIMITED + 1 && !props.showAll, showAddReactionButton: props.canReact, addReactionButtonActive: !!props.addReactionButtonActive, - children: props.children, }); private static readonly computeSnapshot = (props: InternalProps): ReactionsRowViewSnapshot => ({ ariaLabel: _t("common|reactions"), - className: "mx_ReactionsRow", showAllButtonLabel: _t("action|show_all"), addReactionButtonLabel: _t("timeline|reactions|add_reaction_prompt"), addReactionButtonVisible: false, @@ -136,15 +130,6 @@ export class ReactionsRowViewModel this.snapshot.merge({ addReactionButtonActive }); } - public setChildren(children?: ReactNode): void { - this.props = { - ...this.props, - children, - }; - - this.snapshot.merge({ children }); - } - public setAddReactionHandlers({ onAddReactionClick, onAddReactionContextMenu, diff --git a/apps/web/src/viewmodels/profile/DisambiguatedProfileViewModel.ts b/apps/web/src/viewmodels/profile/DisambiguatedProfileViewModel.ts index 49a2db4e29..0617370038 100644 --- a/apps/web/src/viewmodels/profile/DisambiguatedProfileViewModel.ts +++ b/apps/web/src/viewmodels/profile/DisambiguatedProfileViewModel.ts @@ -66,10 +66,6 @@ export interface DisambiguatedProfileViewModelProps { * Optional click handler for the profile. */ onClick?: DisambiguatedProfileViewActions["onClick"]; - /** - * Optional CSS class name to apply to the profile. - */ - className?: string; } /** @@ -83,7 +79,7 @@ export class DisambiguatedProfileViewModel private static readonly computeSnapshot = ( props: DisambiguatedProfileViewModelProps, ): DisambiguatedProfileViewSnapshot => { - const { member, fallbackName, colored, emphasizeDisplayName, withTooltip, className } = props; + const { member, fallbackName, colored, emphasizeDisplayName, withTooltip } = props; // Compute display name const displayName = member?.rawDisplayName || fallbackName; @@ -123,7 +119,6 @@ export class DisambiguatedProfileViewModel return { displayName, colorClass, - className, displayIdentifier, title, emphasizeDisplayName, diff --git a/apps/web/src/viewmodels/timeline/DateSeparatorViewModel.tsx b/apps/web/src/viewmodels/timeline/DateSeparatorViewModel.tsx index 2221d795a1..320e79e48f 100644 --- a/apps/web/src/viewmodels/timeline/DateSeparatorViewModel.tsx +++ b/apps/web/src/viewmodels/timeline/DateSeparatorViewModel.tsx @@ -68,7 +68,6 @@ export class DateSeparatorViewModel super(props, { label: DateSeparatorViewModel.computeLabel(props, relativeDatesEnabled), - className: "mx_TimelineSeparator", }); this.relativeDatesEnabled = relativeDatesEnabled; @@ -101,7 +100,6 @@ export class DateSeparatorViewModel const label = DateSeparatorViewModel.computeLabel(this.props, this.relativeDatesEnabled); return { label, - className: "mx_TimelineSeparator", jumpToEnabled: this.jumpToDateEnabled && !this.props.forExport, jumpFromDate: formatDateForInput(new Date(this.props.ts)), }; diff --git a/apps/web/test/unit-tests/components/structures/__snapshots__/RoomView-test.tsx.snap b/apps/web/test/unit-tests/components/structures/__snapshots__/RoomView-test.tsx.snap index dd83b7d0c5..828ea77ed2 100644 --- a/apps/web/test/unit-tests/components/structures/__snapshots__/RoomView-test.tsx.snap +++ b/apps/web/test/unit-tests/components/structures/__snapshots__/RoomView-test.tsx.snap @@ -717,9 +717,10 @@ exports[`RoomView for a local room in state NEW that is encrypted should match t style="height: 400px;" >
    { await waitFor(() => expect(vm.getSnapshot().state).toBe(EncryptionEventState.ENABLED)); expect(vm.getSnapshot()).toMatchObject({ state: EncryptionEventState.ENABLED, - className: "mx_EventTileBubble mx_cryptoEvent mx_cryptoEvent_icon", encryptedStateEvents: false, }); }); @@ -125,7 +124,6 @@ describe("EncryptionEventViewModel", () => { const vm = createVm(); await waitFor(() => expect(vm.getSnapshot().state).toBe(EncryptionEventState.UNSUPPORTED)); - expect(vm.getSnapshot().className).toBe("mx_EventTileBubble mx_cryptoEvent"); }); it("sets ENABLED_DM with partner display name", async () => { diff --git a/apps/web/test/viewmodels/message-body/DecryptionFailureBodyViewModel-test.tsx b/apps/web/test/viewmodels/message-body/DecryptionFailureBodyViewModel-test.tsx index 85580842de..580dea3a31 100644 --- a/apps/web/test/viewmodels/message-body/DecryptionFailureBodyViewModel-test.tsx +++ b/apps/web/test/viewmodels/message-body/DecryptionFailureBodyViewModel-test.tsx @@ -22,19 +22,6 @@ describe("DecryptionFailureBodyViewModel", () => { }); }); - it("should return the snapshot with extra class names", () => { - const vm = new DecryptionFailureBodyViewModel({ - decryptionFailureCode: null, - verificationState: true, - extraClassNames: ["custom-class"], - }); - expect(vm.getSnapshot()).toMatchObject({ - decryptionFailureReason: DecryptionFailureReason.UNABLE_TO_DECRYPT, - isLocalDeviceVerified: true, - extraClassNames: ["mx_DecryptionFailureBody", "mx_EventTile_content", "custom-class"], - }); - }); - it.each([ { code: DecryptionFailureCode.HISTORICAL_MESSAGE_BACKUP_UNCONFIGURED, diff --git a/apps/web/test/viewmodels/message-body/MessageTimestampViewModel-test.tsx b/apps/web/test/viewmodels/message-body/MessageTimestampViewModel-test.tsx index 864d4ce622..08039003d0 100644 --- a/apps/web/test/viewmodels/message-body/MessageTimestampViewModel-test.tsx +++ b/apps/web/test/viewmodels/message-body/MessageTimestampViewModel-test.tsx @@ -42,14 +42,13 @@ describe("MessageTimestampViewModel", () => { }); }); - it("should return the snapshot with extra class names", () => { + it("should return the snapshot without presentation class names", () => { const vm = new MessageTimestampViewModel({ ts: nowDate.getTime(), }); expect(vm.getSnapshot()).toMatchObject({ ts: "08:09", tsSentAt: "Fri, Dec 17, 2021, 08:09:00", - className: "mx_MessageTimestamp", }); }); diff --git a/apps/web/test/viewmodels/message-body/ReactionsRowViewModel-test.tsx b/apps/web/test/viewmodels/message-body/ReactionsRowViewModel-test.tsx index 46c57d2b21..30cc4be2e7 100644 --- a/apps/web/test/viewmodels/message-body/ReactionsRowViewModel-test.tsx +++ b/apps/web/test/viewmodels/message-body/ReactionsRowViewModel-test.tsx @@ -29,7 +29,6 @@ describe("ReactionsRowViewModel", () => { expect(snapshot.showAllButtonVisible).toBe(true); expect(snapshot.showAddReactionButton).toBe(true); expect(snapshot.addReactionButtonActive).toBe(false); - expect(snapshot.className).toContain("mx_ReactionsRow"); }); it("hides show-all after onShowAllClick", () => { diff --git a/apps/web/test/viewmodels/profile/DisambiguatedProfileViewModel-test.tsx b/apps/web/test/viewmodels/profile/DisambiguatedProfileViewModel-test.tsx index faf2edecfd..d92837e120 100644 --- a/apps/web/test/viewmodels/profile/DisambiguatedProfileViewModel-test.tsx +++ b/apps/web/test/viewmodels/profile/DisambiguatedProfileViewModel-test.tsx @@ -31,7 +31,6 @@ describe("DisambiguatedProfileViewModel", () => { expect(vm.getSnapshot()).toEqual({ displayName: "Alice", colorClass: "mx_Username_color3", - className: undefined, displayIdentifier: "@alice:example.org", title: "Alice (@alice:example.org)", emphasizeDisplayName: true, @@ -47,23 +46,12 @@ describe("DisambiguatedProfileViewModel", () => { expect(vm.getSnapshot()).toMatchObject({ displayName: "Fallback", colorClass: undefined, - className: undefined, displayIdentifier: undefined, title: undefined, emphasizeDisplayName: undefined, }); }); - it("should pass through className prop", () => { - const vm = new DisambiguatedProfileViewModel({ - member, - fallbackName: "Fallback", - className: "mx_DisambiguatedProfile", - }); - - expect(vm.getSnapshot().className).toBe("mx_DisambiguatedProfile"); - }); - it("should delegate onClick without emitting a snapshot update", () => { const onClick = jest.fn(); const vm = new DisambiguatedProfileViewModel({ diff --git a/apps/web/test/viewmodels/timeline/DateSeparatorViewModel-test.tsx b/apps/web/test/viewmodels/timeline/DateSeparatorViewModel-test.tsx index 9abfd76e6e..e3fad0c578 100644 --- a/apps/web/test/viewmodels/timeline/DateSeparatorViewModel-test.tsx +++ b/apps/web/test/viewmodels/timeline/DateSeparatorViewModel-test.tsx @@ -109,7 +109,6 @@ describe("DateSeparatorViewModel", () => { const vm = createViewModel(); expect(vm.getSnapshot().label).toBe("today"); - expect(vm.getSnapshot().className).toBe("mx_TimelineSeparator"); }); it("uses full date when exporting", () => { diff --git a/packages/shared-components/__vis__/linux/__baselines__/message-body/DecryptionFailureBodyView/DecryptionFailureBodyView.stories.tsx/has-extra-class-names-auto.png b/packages/shared-components/__vis__/linux/__baselines__/message-body/DecryptionFailureBodyView/DecryptionFailureBodyView.stories.tsx/has-extra-class-names-auto.png deleted file mode 100644 index 3e5ba0fdb5bcc5415916da00fa52330be2390cb8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20715 zcmai6XF$`*(@tX007{jjQeve^u~DSOihu%&q5=YT1%XJB4v7sF1 ziAy3;%4w5f#5G{XQxZvvWVw39=0I{+Z9u%|mLP+kWrLcxZq0c)@66I4uVxx=+4{gj-TI&?Vt(M(s~6i`cAq^xdr{)jmn*j%JtKB>MSa>p zp6|75#@{dQa2Q(f{=L!u?z}I3p$S860fX0?vt38bDO8#ioxviJ(GPZPZnjIk_7JN>4Tuzk6m7C`DC`!a+E`J3EM~A(J%=@T1s^=X1RnkC~*Z6(w zadF-JJ!zF{VT}sq_r11JFwvzk9e|E;rjz98Bt?){LPwT!E+u=eB^D)Urt?{9HEti2W_Vqc3NUps{ z&rQki(H)mJ>-|yY6{K8L!S~()E=n~`r7;` zkJo+MF05HQ!};gM)6qASM|Qm0d27vb!>pfX#_Au=Q)w-9&RQH}#$9Bep!#h3W7h(m zuBXkO@*AD~-rQ}x?vbo4QaO#xi`}ZEIyUg9K-@w(z$b9*xy8w?1&yAKZCiJIcJBL> z@Yf{wyK=Yal>GMZC6zl%2keE!_xL}kJE8ztX*1E=0F z?#^75**MKxD&b_etDj|HTZp-$%kSU2|_OYV(t8VCnAf--sn<}1mU4Qh=-h)s7jw%!=*#!ShFz8hFZJISR^3w3x z%J&grUVgQ4bpyZdUEa1ozAPX=WM|;e2eoD4hm%fto4b|_PB9;?@41&zV4TZK-Cg*v z#n^UUj&0b!@VI*gL&MQ#js3F+R3*ncwie7RY1rLp>pFa9Ux9+k@Y^-NqT($IjC*fP z?hR)aqbziGO_RKVq_^?+<2}&^Hx<*rb=+&5 zwLhU!DXOz9$w?u6^nzAV;}mmqrQc0$$Gvy-)G}KuOWmW3rE-dY<><#0^yP^c<&1T# z+F2h`ryU>oerMw~+pv|gg}d}T0W;62S#^3!qlJ@-Hh6_LGdtpJ#B3veH?JBsZc=>Q z-D?~mrlu6Ot*ZN9NwMzm@yH@ui-0QzF6lokrhfMh9?2=JwP?$4<;^Wza`X4`p0R?V z16BT4l#7P_I}1N=9iJ0F+G<}EosqY}zE4XcY-eUePQB#*FyB?L|3U zo?+Q&cZsl;vjvjH`ArQQ$_n(wI(xb9`2}PBDRB`mszSaB81?K}bqYWXf3O z$*-^KWGJ+Pe`(a-)=sAKHSbV<6InXZc*;sYDFD^Y*RacBGY)g~+vvkUQ>%IQma znE=NpDYa5%)~~Twb{U=OPfc2-C8s&X9R0Q9m*2>Oc+b54lI91EPhvv11$JCek%a^Q zB-14~B)h-FKq;!syI@eIF=niRy}|8VK*;XY=zw$0FMB2axa4?OwH;G6n`<(3I!0`y z>sd&Z&%0SCa_W9aG-h`ui*FroQs}K%%9n(U5}bis>OE=bSth1{D9UVmND!?dYxjbSc=3P*m-nid9N7LEA|MP5@@xnF})2_^-q@tP{=fJ9NbGp*NdGm8uPAabJ zF6hoH%pc5l%KUIH{eVk#c#h|F(um&WbYF~hlUHh9$-s*HVXGx+;*=32?M{hqUc|Jp@!$9e{fnpOHE55JMR;X2UN zrk_$QrCRj6#NzLZWA|_Lp8lt?zci;Sw3_+I$#d3`g4&ZAIVlddxpfNYb#TCftBF|H zE{7q@YOWs-9~@CQVQMhB{Dk_V_b+nTz0VXocBP(;X|9}qWsE)={AWC4D5-v``N+_g zlG?In*O6A{?^*R9YG#~EXwMz-%6VbGDtFQ-_rs3z);F)b&4+t0H5zz73p*9CtNF%v z-e5tmXHKt9r-w~YNT0(u-jO;5C7;ciALN~mJJpQto0XC~R-ARaA!FpHewTFjm`mu7 zy&Dwc%$vu@y0d%wHhM0qDlL$Ycx8Nj|JdO6#(-|$7@ps!%I{l86GcaRd2Qq0-)&HR za&jo8`&@jJqbu)9%JCaVYG%KPpj|C6JXtua#x1OG=~&T*+>oGO zW;PU)Af8dZ_r9L@tgv4p-8lvU-{NG~)+h5m#1wfSDyVJWNE079eDxh{ee62gbe-9_=E~A>PU>avTx;d-Qw2YD>l7r+TR)dnUvCaR{I79+ zsF%gtMY4MTF z67QFLucxGy{dMrQFsS=(I#ScODtE5Z_}JhrYaP#{J43De3R4UcLc7{qo1J}sj%Fxs zYMv5MGG$QlcY5CUFMW}539h4+DaUz(xz!7UOi#Y!U2Z&KTFg1}b6KUncgeZp-Aw0g zMWfAuZNuj`me(DLdl~YVr?yo;#<95;Qfsl2)jB&#yq5bd2 zPro}J=js26`C_&%Eyd{9DQ2Z{{p?V`>3+@i;t{_;C3ue24cgw!kLdgIQ$Z<^kvZ_q zRiABgeRZ3s#q+G~d;a|Na5c}#N>lE z&;ER=rrKP^ljD5_rS4;OeXPH#v%Z{L$(-untNl8s>%ZN($wSNcPv12NfSg;gwC-Q< z2ZM)7 *WdH$*NK;dDNMU!iIeGzM)tBsuv*k~Iz=o)EZ()E0H{+7Y`zI^4@@iBvZ zPyLg(6iLllix$3;^gAO~>9dac=MFSkM@a2T>djqJFwoEZR%|vy zrLQOQ)%~daruu1Rz?Q20CQ=?}ykFiJAE{TAuS{{u_|YA9``sJw<;z0*3-nJukMoq< zRUSW{)1v#gdTX3y${BCZX!g?l5`)RZnFFI+L!*30bLu2Rd1n_mzcOulQe_dE!>cSa z%Mi_ZGRWJxE9B=Br_7%c84|s`LbK*&;svFq<44!*9O>GpljF5~{%~pi>6o=zLt`y( z6ZBQ9$IUarV~Y1i4;-nk`|ogO^^RYPyUcr4 zr`7p%gv1$F_C|+w8P&~**=dxl+C9BseAWHP9Rc~COGYxkSoI%r=T5?-R1$kT)!CZG zUCUlTDrWsR-mD_sq$X z5p!n^Myq*ceLq(iew#I>&^;8#Z0f9^DG~g2(cGxK!W-SGwkAqV)mgi%Oj}+z7g@&` z>V$SAm?<usk9^Iu*fuEI8yuT) zovg}Oq@+Z~xA5xJ>SW^+Ss%uWrIgHPEFar#IXd#)|8{*OFNUsa)GXV$MeU}HP1xX+ z>&7qp#yL;IYqNX0?we|a4u&Y4yrS&CvhPb*?UAop*LofMUo__n61(>c8r&(B;)kr# z2J^~H3pLb~m7`7moM@b;mbX|bjJKjBQm-UoW>Lp%-Q~=&gMEGHQq1lR9dGBglq;C3 z<+co!N!SK8-Ci|%v3_@SpyjCmk>)4+ReSFAyeKv^d0{&*ywyITysNK#%D9V3MQGk| zRZ&r0$geLZE(KmYPI>=pI{bag2{n`80_LceV#n!n9i%qbk*j_C2&_CRJBqi4yadG0k5x><=-r$9v zPsz&4TJYj!Mpv@mj;W`5yUp5KTOF3~{nXieA!@h!O-Xi~#Mxf@ue2KF=I=V^A}VKg zyOow`yMB^)s!9Ff(RnVxFVF9sve#dy5~qkO`T0FTp`C%DQGYt?Lf!i;OT@=j^lvJ3 zle(8=hbY%CR_?B;I}kN3VWx77fB&=d-4G6*VlVqv+>pl{*Ip~%+aH(_{5N||!Sa&s z5})Yq(6T9o)#Jba&K|4n-d=BbKS-%ECZaEo`SVi8_Q2?3G@>RhiPdgDvZCzYf4qG& zV-EA{-YW*GGIHJb%-r6n?JSmjc_yH;*?j8L|F-XnGWl*9b8u|`1+$?aWhWzsYZO+E z?&+A6(A)Cj;bsXUwTE)WGTLT&R^*HyrN%#{T!xRDi}psi95^V_>-~>vUcKt^tgP?d z3|&+6jJ95hkWi=4&_{uxIm45F1$4K1R%P}7>q59iSn{`m6ss?xk*92UVL}~59O@vF z1?gY*n%qQ!yu%0WtswUyPo zSO`}mvBYcKw-idxTm5LE3KD~^Oa7d!NhhVeZKexNhf2ttREHas9>IBUZcoaQY;1c!1Wv@@(m$r zQdcubE|ew>GG#ukosD4wdvaSr?$!k^vtj!>%0o2*hHVZo{qoBw6p=@)Y(ND4lNMCE zwB7{XKo@9H^WS(?a7ZX00whB<0{ z`R?d&%wVOnbLpg*+bv20eSU}~o+YstiBKCB{TnZ&KjRF=eU4r}i)502X$c`5D(q#J z`vMs$mRggKu#Kp+J9G37DpPvy>?-9eiKxV0C?Y1#-CoN0fU<-@xcnH4R5QuP+o=j? z6{bGb0npug=P=!X+%G82fkMkbp=HX&w3fM?O#=+bp}vCs>Fj*a|7FWOWQBEG7&~y0*oQ&?!*}Dc`0=Bxois)#JvQi1zHE=_ z&%Q{lSc$H)Ko<1x^U=piN1sOCf*{R(0k(dzM~B!=O3{%!23uTR%OKrcTe3_5z)9KE zC1i@d7>#RRn01y<77}|lwad~i7=#Zt-YTp=$CaE@GLKFwd(nn70@t0$`(>fv*UxeNryfnAn258F1NrNM{a0hkvPE zgzvfvXW*tosXw{^CxUE@YrKR;<1T5)!b#n3&vij(xak8f`sQ1SGd(PwDvoUNej(tp z{s6HXiJdI+7{FlbgYf#(lUDE*;uw>)Wa;JSfq61__6i)2RQpQqb?P@TYIWLE8U8x( z;hS8l<0J5{kK*Np>dvrsI`x~21p_K^)OSFPbQ4nd7E8J3v5>f7d9 zQzp#Tdj>EryCFUzpQpm!g@BiSd%^st6t>#P5;+_Q<&$4vX7#vA*h`=0IcL0pF z||Aal|x<@C`)SqC6X5T>e0W?;G~XF-?)3;6&!wJzKz6i2Xyv*^KP%UizG@Zsuu?$;Y|Hg5cOj2D~@Sn!ox`>+W)(_?2R!2#5W z3>nJlDGl?^P8XOOK}`ho*O1)kDZDhj{5ps|^X`dn@~J9(5#hr(@H^ENPvzMI)W<@Y zGv0yN?>Lv|GC0%79s>8$CeT)wl+?H@tmS2GBvQItv(`i!MY`7lOVPN`5TDq*T{_^61Umn49t&9?(>EL)> zINb>pW_{DZ!~?K`DkIK4rdUPXJeY&k1moM!{LgX15}YNV{1QiTTg`@0Oi@fNKMhd+ zfrlrhSzI{EVMl}V(QIv77T1tc3il?iRXGGSHF-~zVeQmx)@?rId7ylrUNuT{2brGb z1mwD~tM0)O)Mo4wkzCAm)BXVDgQq;TaLE54{SaPPTcMfrI^=Uj5{XB@c@-9^4 z!4B@(PlB3}b{T2}fQ%AKuo}aS4*rQASS|3|iS~?2>PbL5GA#rAJzwN!qU`~8Fxf6V zPMF9X752Zza;(UktiSwIg&@gMV6!JNbxpsaIts9*k^^76QbL56U@^Rbu-+B=j+zZd zLOW82f!`w)=?d&l5fj7@6DaKmwf8ftrRh>4p@i<>M}XeH>T9B@v>h~XIrDZ`c^o{hWG83B`E+=%kPe-7!szI4CfOe6EvD5g^yY=TqOi5)SZ zw%J3pb|LYl>_Q?o#+YynqP1fmDl?u$3mZ)D4*2yu`FS2P&NO+XR*_%qk}1oM%% zgFE~*c;eF%Yf%S;5$E@GyJc)Y5ZiD6?ZpH+U;!UlJMWJm_Q&RpO6dOharrI1E^iar zuwV2+48c2;6pqsEhNO&5x7YA@L_cuf!#(%@_@jjlQo^gy=0WhPw&cuju@887hDoyt zf>HbHCmpS1%F%EDH|S|F&FN{Yji_&>%8`Hs+k^cFDEw#NtvMjAf}J@r&NvTZpWnT4 zIt=`lvm;IpeG8!d`AuRJBDT+TJNyjCrUKeM%!}BS_*=#fqxL!{Ks&_jA?_%Br0q}# zF;5r7HlHIxxP^}jc1ZB$13tv}>iL;`;%t()n?>bW+=54SxhOG!en}T@039 zGV}5#6f%Y*1?P1TSURZ0`Wfz0G$|bj@7P@cS=ZWoZn){WJrqr3xo8kO+TNb;j=8av zfdve@J&673xkelxGOixw$o$w85WBl;GvVrx1?njS?;Jtw;LtO|i4G%zlGQzr)MmD# ze}Y5iZlf%j-0)<8IU%s5Nhvx*)V7zJEQB2heF%g;wDywAznVf$n#HgIp=~~G^01<~ z3%M~mT&#v^Y>g|7nUsf!&KM116F}(t_nX(VxaE`qzKpLofzUpO&VUiQOv(|$mMxlq z^>QPBL};!QWdOI?VtK$?<>p5W>jKIV%r-&b!-C3e`*EyYDFcW+>@J{4mqj8$L&hNG z2m+NTh%IVgA#mSUC|?#ZI8t!u#IA4`a^D6NhxvBkzMV@K`U|`7YRVTaaNj!a@z;g) zW-&r2ca!FkuIO+4JHdT(|D(uGZrEGdGl!4_gJhixV7+H`>HlC2LLaiIjA%R};3)`Q z3PP8DvJXk13<%kP?h9D=ku?(-=9D7>Sz-$S>+z}n@V!dP7e3bNz-ygdXRjk51W+6Z ztScz9^j8+6bBM#l zQ3NTjZcMK~CC7JnFu@KoG3Sp<`#;`eJrkO*z8<7F#Q8S6JesvdFn$qktt$rh%F=Yb zN32f%I%nY4ic)%X4<_p6v9yTsyKrmWwQ+Doz|%X&^1#+%9|fBqjeN8W1yAo9D-GlK zR{-y8-)B5r*Gp#gA@ezbAU03hg9!do(^*Sk+zv_?&P5md`Q?Vx>#W^W+Ga5B*2M=r z2|o|PwhQ+WwF8p5&TG$X2*E(0ZJG^1TVTg6&0i*X?qr3;pZ}1Yt6vCL_eg9jk;~Ku zE4imwB!wwLro4ocC~ncUXaduxYtcyx#^2t|LUV9pD%A=Ov-Qp7x&_iqA`3*E#R*vA zgygmm>ML2MJbxYbZ@}pH{>Q5*R6M;v3CIxxFwQLUI1gXc5;r7HY2q3NDS7WlEQXQ>O<<4^bR6O zK=*WqBkXZK-9ZRS4OF?n* zcC7pdRD+bvxwEAoOwV2};)gR}GZZ|wnuI1HD>6JO!M zw!eTv!nnYmN&SP;b~cS0E-v5)7I@AC3-9a^9B9S^%J3Y>0Ks`)=@Ej4{hR8y+KmnM zTT&6Qgue{!;ylikmk_+|k}MMpA_r+IyChqRWiYpw!lxPX;i9_TR+7%)qrpOs7BqbV zEO~PEt)ZoY(ArXo+X1zpVgLSQoIWVdnox4m8W$-K$1@3heFka37{I~O2;9cRAjnj~EnipjLW{3Rka!}}@%RqN{qDa&p}KrBrDFlZZ9Zg$^Jk<8 zxI%~9{EX;h@GU7vnJ~DQl9Ux4HS+v0AAdFC4j!nTo8U%}}jITs%P z!w(eD4_KAxUe>9H6HL|sMMz$-cnm+6fRzy(<>69_GBlZOcZOvq^o4B@Md-@)#*^5i zB7TAm+-872Gp6gp>8I}}KjW*l5N-+G?xrn>fUoTtME!&<2d+o%pc1xMmS`d#!EW0C z>DPwhMVLNxRq`dwuPg(iEMqAV=ZI*4+y;TTOZDp+e9#YyCq{$tyFvEdi;Pa=d;q8( zQaXU(&LE_-Mw`&}7JHL*hPEDvVaxiySs2AT^Ej8^z^-p{<0rb~gS7R|CrBoXt_e;R z0uSAS{U5}evP9~DL-PmhIapXzwm>M9fIqP>f&P~~mEKr0@Msnjb=wwD=u+_tN)|0q z_#8e1NuC{7Qe)7=$Z`S_D6WDWd(s;X(0|=_hM@k{4Ei@48KJQNl2s5)KLq+8(p1AR z6_kM2gOIkjzB}Wn6D44_CYaA& z1SM!ngVOQO{3{y$qa~bC%d;4()?O?KTHhO&@2 z>w)XGnypaFI;jBR6+sWS23SL*+!_16;eA+BW{)-dGiTnu8 z83zglnR08R1PZlVakHtkr}Lr8J0Biu1PXmqNS(u$2cya_(iR4WtuONUGX#n?$@f3= zjf#BWD6_w8m1a4qJ)`rrM?Tz9kKIbL*=V;xur^GOJpm!|%d7g+fAAj!P(!T;A7Ja< z_BLFkODGH_v9AG)*LcqBIl5%5c!%u)K_AKF5eEXlb{DRMU8F^eet3&Lho11B|3)cA)WnR5y`BE24Qpem2X6Vl;NSilR-ZtoT~_EJes#bx zfL#sTXQ3Qs-=*Bd!3+cC!`!QJ%4fQ8=b|!|r-7lDWd0;1-^fLKLjC~agWg^ixZI4N zlwqJJmk!>F-lb2(JyMBV8EsVBRPZvA^SxaKN-h&?zs6<3WEL--xUUgcqC>8gfTTsT z(>p*g8OqLc^a2woJq>g1giS-G{h6(Ii1h3BDxv2u)cg>Yo2*pqFdrejD=IzIWGKvt zFGm~~HV_3=6vJRSDOSk$^cn0RvboTaY=^kvms1Tm;^SNbYfzB{Qj^-#=P@{5SnL4Aa#W%#~T-(>jsnE{^%nrvWKaDcrq|m113N4#%9@FV7Z{Xa=9>>J1G@; zHUoL>B3U}fxErLMfxH=sga~%otTFg=>~Zj;FBAojT#&?lyz%C^?lRDP#=%vB-s2rp!)r#)1IO7 zvd>fhU=g|uyu--OI^~0@&R$IQL+{TY5Lf&tS&j6IqD-n0sj!7@=E2dMSA0vIASF9g z1gd}#FjANgFKiTaNwJkgD%R6?g4m6X>VnjX#LlH|jsuq&KM=Z^uLnX3XX^PBdy-A~ zYO#rKM4y^HjqADjz%*h<4A=6ba2hF1R@Qh+2w{YeP*(VKZOy?;?UBMK#1gczJhy7m zcUrs9V-dRouBD0vw^SzfrZA3zHb+-&gvunImS9GJ<`Kh^tVF~sW{D*dCDGW&piS2S ztVw1_H1R67FGQFmo1o?HSR7|0ns^BvpQi)-GoydUFU}xZB2qY@euQ-GQ^G%D#Ho8A zo`^WS?clk#pSApl&p)g!#TmqQUU1h&0EvcJ;%UZO5PNOWCc?iVRzE13xBwpf3{!$0 zTt@fDae6@8vo(?wU;BExP+ATXKGB}eV%>khUB=%L{lJ+5u5!&DYvQ^`IRTYv3Nt(H zP152CEh+Sau-}hfbsZud!3-7)@8a>p$15!8Q^?B%-sm+0s;_SAtmaK@NNJf3)z?L8 zTVNtQHuw|!{s-1gn{jC+0u$YoEQgO9fpv{GSSMj-noq_l{vKd@KlvUJsN>xzjBpS; ze2G0#A#yuF9s%z~w+6A_J<;F?(r$)iV_AAEgjeyMEfxH*m4W^`?)5ehJ222nC=x+h zQNBZIrOnj*Gv6D6oE%+t*Pt{jP4(Z&!rq19ro{eA=`l1moIq4~g`z{-h932b03$em z3^NegEX^uuQ$N9UWHCCtpBp|ptf5aNAvo4?AawPM3TwIuSt$H@Z3jX#4_GZ=xNRfn z@MUZPC){RaumuL()XDst;y<_y(v)u$B3KJGp%_72ZDecv6XS&~IRv!<`#t!D_frz_ zhir~2IR}xGF$Q9fEvX=~0geY5X;I+NfKI0txgRAciFKer?o4uiJFg%0KiK->WDgnj zbx`*_aMe)|yf8RB$d4|~hq~u7+gf2fINI!{FVd{2Sr1N5#M+4chSKAs6)5D15UBP5 z*0y@{?oY%zAB4_N>&)>PA$W&k?EzSK_G;vtlZ}POp>2;8KSKR%JF)ph9eAzdcFGZy z#S#z92VVqUUs+_a1Hn{_jP*T$AZLKrQ8VtYMfTx@k+pDn<^!+iZ?MPu2nC%dg~02= z$q(^dg`-Sve$j>nHB~SPt5#poPlrAzI5bAwKr6a?| zrn$;*VprQ+(EHuBz126i359oL${Nsn%{lueKSj3*iV`IDN6`D@f`_UUPue!2M-n#Z z&0h5xymgvrCja85`^9!qK1(5vo%3J%~y{t3AYSo3#Hi% zjywo**|Jj*2XlQWE_3mM=`-QWe;2u|0|JrG>lt3e(umA@x^}g;p!!duQEz8q}4=W>emVNM=5m(+;sxdFC0O#bA)8*RN8bftAeRZ$vNGI^+MxT zpq_O?>KL>|HYf?k<=`@so4OrZGau_T@EZXb$^gHz?-%6dUbxK!DVkp2=z;dZ=qvPy z;qXKC(D~ETeoSk5qAIKjF^JZz%p;iAL{*p(21|!6sU$o#D)d804Ie4x+#>)&tHNwY za6gV}DMYx5RfRdbK!&D9529)e{s^kVNf6B@hm2(lyKWVB2Q-*G8kZyC$e<8G47L)i zaWxGMKQYLFL3xbN<4tq|@lSq_>lZ%g)|P&?$xiP}2H5j6Yu!2@Y>LNW-9+=*Bv z7+P+CjM-E_Ym5+hCD;tL0K7{ipb}!0VC;+Pu8c^OX#7erKE9pd4dcRkdQ0Vx>O0NK+;^UeP zOSSnFh7TNf0HdR~vLY8tA8;_B`!D&g0b|^s){cf)8130~(#U-HP9S-j!0Dd?<8AH% zSIG7e@k2cWB19@wmg;DYXluwtb&eW-61ebm(9bDAG$wB~9 z;cy@pVAC$n;YgxiPTdUkO3fz1iu4GwGNK4~F|4>)-GyK6f4!Z7+^^f0B%VU*kU_oHu-J+(Bl0D-2nbG@Drr5j#e^ykF+!B7 z$y~dWEFGcctJVXcXJ3dzBf1VJT;N#VfxCbA0KJ=s#1i?fjcy{4?};etB7q9DC%6;o z@nd-ANjcO^L#g$eUlQg*4$||`L;^P}za;#4K1`qAVu&%3UlQgj!gR%THvFh!9aa)n zghwcqvNu9)Jy=P2H|Vt6-bnB=L4xd|Vioizk^^P5vHnD23y8+nt3zZ#YxNUo31(Th z!tM7=D3X^DeU3QvGsLFLv6lSVx)@&oBheoKeUMsPq@ONiAOL*}Tr*pkN^dP0Sip-r zrfooYm)hcu6e6d*o_hBNBOL>8GJ&fVIC|lX5%HR+m&+1Ews3>j(m=7L zSNNQps^Nw>?e%H^S6i#50Inih)g==90SN!#hx0mIM}!H`XEIkW3WSfk=|Nlq$nNC{ ztQ-5lLF}jc5eE#%akk!w3Z4D#;8|*jF$Q;@s- zqHp0}d8pXhaCEhIIX)yh-Eqtz=OSnBuze&=bh@LHq2v!Sa)yoj3aE2qo$feAb-+#O zY)*nA8P@5J`3rhElq74UAMZuPMpF(P^K6KTa?+k*E)eN-$1%STck}&Rc|2NsCC9|t z)7zmX=a*yEFs={#nMgdEHktzFru1rlW(hx#L3(b=V_?^qv>gJ{E!1s*XVzfYHRN_W zkuxD&w-zx`E58rZ@2BSprc2S8c`OT4xCBg(RTIeshQKYxxGRM@-a4UOio_;U&x>&< z{qeRIvM2O(tcJ4C{&G$JwS)3g0!%D%Slje8%U9T(W zI%L&Y=0R9U5-cRS7pVce?Im0Ag%5^kB;;s1+WWdttpRKD0Z)oofw3z+ivLdt5mjJp zRXyDXte;y@1xB4WTS^i6ub>Ky`H2c`P6$XUR0YOnJBs|-z!I(kW2@UGu%bmLRDm%} z-DS!NO1suE(>;|u-{9bo9zYu&9 z9zX@X?)*ZqC@@=e>I&inF3yw7a3#yz zYpHFYcyCUID|yO`6c3MDfxiQOZUfb~MdVb@;n)(A!#b0J*i%&56xu~yj!*`x0jg`P zUoj0jV-|7@i23Z*p!({`|4xO0iy9pKmkTV$a!`GFnw=;j*G2x16zLkE`h&yjSln`P zE=Lw!Cl-R{_;0H#aMg+alyy)lvtg?9!`&8bPB6^BqX8v!BZV#eI!KEq$BRNe52dmT z%I69Nu8b*y zaY4U3o`+z<2=%)Qx26d9yT9JR$ivoQOz3w7(lma&HF176Pe`ejEU2fEIZp?wXK zi)+)?XJK;Tc&wrqXi^(S_uZTLqrs~yIiE=;FUNEycxN&N`6zDaPw62Mgn`%QDn6b`9Pd8F z(+=z#M2GtW8ur5vY$ZX5dy#k|-?UIyKRiU*?F~}dp6>rHC+SZC~LvY^<)Y|)g zqoAX2hv+uIeHWbXU27jm3vmA_zU?1|C1qTdL>eE@IAL%Q_M43CH;EMd(W)j(r7sgk ONS14CRwu7?JNkdZ`jqzo diff --git a/packages/shared-components/__vis__/linux/__baselines__/message-body/MessageTimestampView/MessageTimestampView.stories.tsx/has-extra-class-names-auto.png b/packages/shared-components/__vis__/linux/__baselines__/message-body/MessageTimestampView/MessageTimestampView.stories.tsx/has-extra-class-names-auto.png deleted file mode 100644 index 9b784175afe42c8efc080e21a61a63a1f66b98b7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17284 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 Z*!ZngtFw|z;^7H4dW4@>_Au4z{{uZhMd| { +const EncryptionEventViewWrapperImpl = ({ + className, + ...rest +}: EncryptionEventProps & { className?: string }): JSX.Element => { const vm = useMockedViewModel(rest, {}); - return ; + return ; }; const EncryptionEventViewWrapper = withViewDocs(EncryptionEventViewWrapperImpl, EncryptionEventView); diff --git a/packages/shared-components/src/event-tiles/EncryptionEventView/EncryptionEventView.test.tsx b/packages/shared-components/src/event-tiles/EncryptionEventView/EncryptionEventView.test.tsx index 214890b8ee..f71b07f99b 100644 --- a/packages/shared-components/src/event-tiles/EncryptionEventView/EncryptionEventView.test.tsx +++ b/packages/shared-components/src/event-tiles/EncryptionEventView/EncryptionEventView.test.tsx @@ -36,9 +36,8 @@ describe("EncryptionEventView", () => { state, encryptedStateEvents, userName, - className, }); - render(); + render(); }; it("renders Default story", () => { diff --git a/packages/shared-components/src/event-tiles/EncryptionEventView/EncryptionEventView.tsx b/packages/shared-components/src/event-tiles/EncryptionEventView/EncryptionEventView.tsx index 218ae74da6..afa7eb8356 100644 --- a/packages/shared-components/src/event-tiles/EncryptionEventView/EncryptionEventView.tsx +++ b/packages/shared-components/src/event-tiles/EncryptionEventView/EncryptionEventView.tsx @@ -7,6 +7,7 @@ import React, { type JSX } from "react"; import { LockSolidIcon, ErrorSolidIcon } from "@vector-im/compound-design-tokens/assets/web/icons"; +import classNames from "classnames"; import { type ViewModel, useViewModel } from "../../viewmodel"; import styles from "./EncryptionEventView.module.css"; @@ -35,8 +36,6 @@ export type EncryptionEventViewSnapshot = { encryptedStateEvents?: boolean; /** Display name for DM partner, used by ENABLED_DM subtitle text. */ userName?: string; - /** Optional CSS classes passed through to EventTileBubble. */ - className?: string; /** Optional timestamp element rendered in the EventTileBubble footer slot. */ timestamp?: JSX.Element; }; @@ -52,16 +51,35 @@ export interface EncryptionEventViewProps { */ vm: ViewModel; /** - * Ref forwarded to the root DOM element. + * Optional CSS classes passed through to EventTileBubble. + */ + className?: string; + /** + * Optional Ref forwarded to the root DOM element. */ ref?: React.RefObject; } -export function EncryptionEventView({ vm, ref }: Readonly): JSX.Element { +/** + * Renders a timeline bubble describing an encryption-related room event. + * + * Text and icon are selected from `snapshot.state` with optional context: + * - `encryptedStateEvents` switches to state-event specific wording. + * - `userName` is used for DM-specific subtitle text. + * - `timestamp` renders in the bubble footer slot. + * + * Use `className` for host-level styling, following the default React pattern. + * + * @example + * ```tsx + * + * ``` + */ +export function EncryptionEventView({ vm, ref, className }: Readonly): JSX.Element { const { translate: _t } = useI18n(); - const { state, encryptedStateEvents, userName, className, timestamp } = useViewModel(vm); + const { state, encryptedStateEvents, userName, timestamp } = useViewModel(vm); - let icon = ; + let icon = ; let title = encryptedStateEvents ? _t("common|state_encryption_enabled") : _t("common|encryption_enabled"); let subtitle = ""; @@ -86,14 +104,20 @@ export function EncryptionEventView({ vm, ref }: Readonly; + icon = ; title = _t("timeline|m.room.encryption|disabled"); subtitle = _t("timeline|m.room.encryption|unsupported"); break; } return ( - + {timestamp} ); diff --git a/packages/shared-components/src/event-tiles/EncryptionEventView/__snapshots__/EncryptionEventView.test.tsx.snap b/packages/shared-components/src/event-tiles/EncryptionEventView/__snapshots__/EncryptionEventView.test.tsx.snap index 09e39927bb..5b67edede9 100644 --- a/packages/shared-components/src/event-tiles/EncryptionEventView/__snapshots__/EncryptionEventView.test.tsx.snap +++ b/packages/shared-components/src/event-tiles/EncryptionEventView/__snapshots__/EncryptionEventView.test.tsx.snap @@ -3,9 +3,10 @@ exports[`EncryptionEventView > renders Default story 1`] = `
    renders Default story 1`] = ` exports[`EncryptionEventView > renders DisableAttempt story 1`] = `
    renders DisableAttempt story 1`] = ` exports[`EncryptionEventView > renders EnabledDirectMessage story 1`] = `
    renders EnabledDirectMessage story 1`] = ` exports[`EncryptionEventView > renders EnabledLocalRoom story 1`] = `
    renders EnabledLocalRoom story 1`] = ` exports[`EncryptionEventView > renders ParametersChanged story 1`] = `
    renders ParametersChanged story 1`] = ` exports[`EncryptionEventView > renders StateEncryptionEnabled story 1`] = `
    renders StateEncryptionEnabled story 1`] = ` exports[`EncryptionEventView > renders Unsupported story 1`] = `
    renders Unsupported story 1`] = ` exports[`EncryptionEventView > renders WithTimestamp story 1`] = `
    { +const DecryptionFailureBodyViewWrapperImpl = ({ + className, + ...rest +}: DecryptionFailureBodyProps & { className?: string }): JSX.Element => { const vm = useMockedViewModel(rest, {}); - return ; + return ; }; const DecryptionFailureBodyViewWrapper = withViewDocs(DecryptionFailureBodyViewWrapperImpl, DecryptionFailureBodyView); @@ -40,7 +43,7 @@ const meta = { args: { decryptionFailureReason: DecryptionFailureReason.UNABLE_TO_DECRYPT, isLocalDeviceVerified: true, - extraClassNames: ["extra_class"], + className: "extra_class", }, } satisfies Meta; @@ -49,24 +52,17 @@ type Story = StoryObj; export const Default: Story = {}; -export const HasExtraClassNames: Story = { - args: { - decryptionFailureReason: DecryptionFailureReason.UNABLE_TO_DECRYPT, - extraClassNames: ["extra_class_1", "extra_class_2"], - }, -}; - export const HasErrorClassName: Story = { args: { decryptionFailureReason: DecryptionFailureReason.UNSIGNED_SENDER_DEVICE, - extraClassNames: undefined, + className: undefined, }, }; export const HasErrorBlockIcon: Story = { args: { decryptionFailureReason: DecryptionFailureReason.SENDER_IDENTITY_PREVIOUSLY_VERIFIED, - extraClassNames: undefined, + className: undefined, }, }; @@ -74,7 +70,7 @@ export const HasBackupConfiguredVerifiedFalse: Story = { args: { decryptionFailureReason: DecryptionFailureReason.HISTORICAL_MESSAGE_BACKUP_UNCONFIGURED, isLocalDeviceVerified: false, - extraClassNames: undefined, + className: undefined, }, }; @@ -82,6 +78,6 @@ export const HasBackupConfiguredVerifiedTrue: Story = { args: { decryptionFailureReason: DecryptionFailureReason.HISTORICAL_MESSAGE_BACKUP_UNCONFIGURED, isLocalDeviceVerified: true, - extraClassNames: undefined, + className: undefined, }, }; diff --git a/packages/shared-components/src/message-body/DecryptionFailureBodyView/DecryptionFailureBodyView.test.tsx b/packages/shared-components/src/message-body/DecryptionFailureBodyView/DecryptionFailureBodyView.test.tsx index b886584c35..954f910550 100644 --- a/packages/shared-components/src/message-body/DecryptionFailureBodyView/DecryptionFailureBodyView.test.tsx +++ b/packages/shared-components/src/message-body/DecryptionFailureBodyView/DecryptionFailureBodyView.test.tsx @@ -5,26 +5,23 @@ * 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 { DecryptionFailureBodyView, DecryptionFailureReason } from "./DecryptionFailureBodyView"; import { MockViewModel } from "../../viewmodel"; -import * as stories from "./DecryptionFailureBodyView.stories"; - -const { HasExtraClassNames } = composeStories(stories); describe("DecryptionFailureBodyView", () => { function customRender( decryptionFailureReason: DecryptionFailureReason, isLocalDeviceVerified: boolean = false, - extraClassNames: string[] | undefined = undefined, + className: string | undefined = undefined, ): ReturnType { return render( , ); } @@ -38,12 +35,14 @@ describe("DecryptionFailureBodyView", () => { ); } - it("Should display with extra class names", () => { - // When - const { container } = render(); + it("applies custom className to the root element", () => { + const { container } = customRender( + DecryptionFailureReason.UNABLE_TO_DECRYPT, + false, + "extra_class_1 extra_class_2", + ); - // Then - expect(container).toMatchSnapshot(); + expect(container.firstChild).toHaveClass("extra_class_1", "extra_class_2"); }); it.each([true, false])(`Should display "Unable to decrypt message and device verification is %s"`, (verified) => { diff --git a/packages/shared-components/src/message-body/DecryptionFailureBodyView/DecryptionFailureBodyView.tsx b/packages/shared-components/src/message-body/DecryptionFailureBodyView/DecryptionFailureBodyView.tsx index 8ea36dc1b6..a3f99e1ac6 100644 --- a/packages/shared-components/src/message-body/DecryptionFailureBodyView/DecryptionFailureBodyView.tsx +++ b/packages/shared-components/src/message-body/DecryptionFailureBodyView/DecryptionFailureBodyView.tsx @@ -65,14 +65,14 @@ export interface DecryptionFailureBodyViewSnapshot { * The local device verification state. */ isLocalDeviceVerified?: boolean; - /** - * Extra CSS classes to apply to the component - */ - extraClassNames?: string[]; } /** * The view model for the component. + * + * Snapshot data is intentionally content-focused (`decryptionFailureReason` + * plus optional `isLocalDeviceVerified`). Container styling is supplied + * via component props. */ export type DecryptionFailureBodyViewModel = ViewModel; @@ -81,6 +81,10 @@ interface DecryptionFailureBodyViewProps { * The view model for the component. */ vm: DecryptionFailureBodyViewModel; + /** + * Optional CSS class names to apply to the component container. + */ + className?: string; /** * React ref to attach to any React components returned */ @@ -156,17 +160,27 @@ function errorClassName(decryptionFailureReason: DecryptionFailureReason): strin } /** - * A placeholder element for messages that could not be decrypted + * Renders a message-body placeholder for events that could not be decrypted. + * + * Message copy and warning styling are derived from snapshot values: + * - `decryptionFailureReason` selects the base text/variant. + * - `isLocalDeviceVerified` influences historical-backup messaging. + * + * Use `className` for host-level container styling, following standard React patterns. * * @example * ```tsx - * + * * ``` */ -export function DecryptionFailureBodyView({ vm, ref }: Readonly): JSX.Element { +export function DecryptionFailureBodyView({ + vm, + ref, + className, +}: Readonly): JSX.Element { const i18nApi = useI18n(); - const { decryptionFailureReason, isLocalDeviceVerified, extraClassNames } = useViewModel(vm); - const classes = classNames(styles.content, errorClassName(decryptionFailureReason), extraClassNames); + const { decryptionFailureReason, isLocalDeviceVerified } = useViewModel(vm); + const classes = classNames(styles.content, errorClassName(decryptionFailureReason), className); return (
    diff --git a/packages/shared-components/src/message-body/DecryptionFailureBodyView/__snapshots__/DecryptionFailureBodyView.test.tsx.snap b/packages/shared-components/src/message-body/DecryptionFailureBodyView/__snapshots__/DecryptionFailureBodyView.test.tsx.snap index 7bd5899caf..e5b1bcd1eb 100644 --- a/packages/shared-components/src/message-body/DecryptionFailureBodyView/__snapshots__/DecryptionFailureBodyView.test.tsx.snap +++ b/packages/shared-components/src/message-body/DecryptionFailureBodyView/__snapshots__/DecryptionFailureBodyView.test.tsx.snap @@ -40,16 +40,6 @@ exports[`DecryptionFailureBodyView > Should display "Unable to decrypt message a
    `; -exports[`DecryptionFailureBodyView > Should display with extra class names 1`] = ` -
    -
    - Unable to decrypt message -
    -
    -`; - exports[`DecryptionFailureBodyView > should handle historical messages with no key backup and device verification is false 1`] = `
    { +const MessageTimestampWrapperImpl = ({ + onClick, + onContextMenu, + className, + ...rest +}: MessageTimestampProps & { className?: string }): ReactNode => { const vm = useMockedViewModel(rest, { onClick, onContextMenu, }); - return ; + return ; }; const MessageTimestampWrapper = withViewDocs(MessageTimestampWrapperImpl, MessageTimestampView); @@ -69,12 +74,6 @@ export const HasInhibitTooltip: Story = { }, }; -export const HasExtraClassNames: Story = { - args: { - className: "extra_class_1 extra_class_2", - }, -}; - export const HasHref: Story = { args: { href: "~", diff --git a/packages/shared-components/src/message-body/MessageTimestampView/MessageTimestampView.test.tsx b/packages/shared-components/src/message-body/MessageTimestampView/MessageTimestampView.test.tsx index f51b7ecc8b..f04d7d585e 100644 --- a/packages/shared-components/src/message-body/MessageTimestampView/MessageTimestampView.test.tsx +++ b/packages/shared-components/src/message-body/MessageTimestampView/MessageTimestampView.test.tsx @@ -21,7 +21,7 @@ import { MockViewModel } from "../../viewmodel/MockViewModel.ts"; import { I18nContext } from "../../utils/i18nContext.ts"; import { I18nApi } from "../../index.ts"; -const { Default, HasHref, HasExtraClassNames } = composeStories(stories); +const { Default, HasHref } = composeStories(stories); const renderWithI18n = (ui: React.ReactElement): ReturnType => render(ui, { @@ -38,9 +38,16 @@ describe("MessageTimestampView", () => { expect(container).toMatchSnapshot(); }); - it("renders the message timestamp with extra class names", async () => { - const { container } = render(); - expect(container).toMatchSnapshot(); + it("applies custom className to the timestamp element", async () => { + const vm = new MockViewModel({ + ts: "04:58", + tsSentAt: "Thu, 17 Nov 2022, 4:58:32 pm", + }); + + renderWithI18n(); + + const target = screen.getByText("04:58"); + expect(target).toHaveClass("extra_class_1", "extra_class_2"); }); it("renders the message timestamp with href", async () => { diff --git a/packages/shared-components/src/message-body/MessageTimestampView/MessageTimestampView.tsx b/packages/shared-components/src/message-body/MessageTimestampView/MessageTimestampView.tsx index 7ddb4a082c..a60f68e882 100644 --- a/packages/shared-components/src/message-body/MessageTimestampView/MessageTimestampView.tsx +++ b/packages/shared-components/src/message-body/MessageTimestampView/MessageTimestampView.tsx @@ -32,10 +32,6 @@ export interface MessageTimestampViewSnapshot { * If set to true then no tooltip will be shown */ inhibitTooltip?: boolean; - /** - * Extra class name to apply to the component - */ - className?: string; /** * If specified, will be rendered as an anchor bearing the href, a `span` element will be used otherwise */ @@ -55,6 +51,9 @@ export interface MessageTimestampViewActions { /** * The view model for the message timestamp. + * + * Snapshot data describes timestamp content and rendering behavior, while + * container styling is supplied via component props. */ export type MessageTimestampViewModel = ViewModel & MessageTimestampViewActions; @@ -63,6 +62,10 @@ interface MessageTimestampViewProps { * The view model for the message timestamp. */ vm: MessageTimestampViewModel; + /** + * Optional CSS class name to apply to the component. + */ + className?: string; } /** @@ -70,17 +73,18 @@ interface MessageTimestampViewProps { * * The view model provides the timestamp values and display options. The component * can render as a link when `href` is set, and can show both sent-at and received-at - * times in the tooltip when `tsReceivedAt` is provided. + * times in the tooltip when `tsReceivedAt` is provided. Use `className` for + * host-level styling. * * @example * ```tsx - * + * * ``` */ -export function MessageTimestampView({ vm }: Readonly): JSX.Element { +export function MessageTimestampView({ vm, className }: Readonly): JSX.Element { const { translate: _t } = useI18n(); - const { ts, tsSentAt, tsReceivedAt, inhibitTooltip, className, href } = useViewModel(vm); + const { ts, tsSentAt, tsReceivedAt, inhibitTooltip, href } = useViewModel(vm); const onKeyDown = (event: KeyboardEvent): void => { if (vm.onClick) { diff --git a/packages/shared-components/src/message-body/MessageTimestampView/__snapshots__/MessageTimestampView.test.tsx.snap b/packages/shared-components/src/message-body/MessageTimestampView/__snapshots__/MessageTimestampView.test.tsx.snap index 67ae66820e..4b896472f5 100644 --- a/packages/shared-components/src/message-body/MessageTimestampView/__snapshots__/MessageTimestampView.test.tsx.snap +++ b/packages/shared-components/src/message-body/MessageTimestampView/__snapshots__/MessageTimestampView.test.tsx.snap @@ -12,18 +12,6 @@ exports[`MessageTimestampView > renders the message timestamp in default state 1
    `; -exports[`MessageTimestampView > renders the message timestamp with extra class names 1`] = ` -
    - - 04:58 - -
    -`; - exports[`MessageTimestampView > renders the message timestamp with href 1`] = `
    { +}: WrapperProps & { children?: ReactNode; className?: string }): JSX.Element => { const vm = useMockedViewModel(snapshotProps, { onShowAllClick: onShowAllClick ?? fn(), onAddReactionClick: onAddReactionClick ?? fn(), onAddReactionContextMenu: onAddReactionContextMenu ?? fn(), }); - return ; + return ( + + {children} + + ); }; const meta = { diff --git a/packages/shared-components/src/message-body/ReactionRow/ReactionsRow.test.tsx b/packages/shared-components/src/message-body/ReactionsRow/ReactionsRow.test.tsx similarity index 83% rename from packages/shared-components/src/message-body/ReactionRow/ReactionsRow.test.tsx rename to packages/shared-components/src/message-body/ReactionsRow/ReactionsRow.test.tsx index a6f28b9370..2ca1c53281 100644 --- a/packages/shared-components/src/message-body/ReactionRow/ReactionsRow.test.tsx +++ b/packages/shared-components/src/message-body/ReactionsRow/ReactionsRow.test.tsx @@ -68,7 +68,6 @@ describe("ReactionsRowView", () => { showAddReactionButton: true, addReactionButtonLabel: "Add reaction", addReactionButtonVisible: true, - children: 👍, }, { onShowAllClick, @@ -77,7 +76,11 @@ describe("ReactionsRowView", () => { }, ) as ReactionsRowViewModel; - render(); + render( + + 👍 + , + ); await user.click(screen.getByRole("button", { name: "Show all" })); await user.click(screen.getByRole("button", { name: "Add reaction" })); @@ -87,4 +90,16 @@ describe("ReactionsRowView", () => { expect(onAddReactionClick).toHaveBeenCalledTimes(1); expect(onAddReactionContextMenu).toHaveBeenCalledTimes(1); }); + + it("applies custom className to the toolbar container", () => { + const vm = new MockViewModel({ + ariaLabel: "Reactions", + isVisible: true, + addReactionButtonLabel: "Add reaction", + }) as ReactionsRowViewModel; + + render(); + + expect(screen.getByRole("toolbar", { name: "Reactions" })).toHaveClass("custom-reactions-row", "another-class"); + }); }); diff --git a/packages/shared-components/src/message-body/ReactionRow/ReactionsRowView.tsx b/packages/shared-components/src/message-body/ReactionsRow/ReactionsRowView.tsx similarity index 94% rename from packages/shared-components/src/message-body/ReactionRow/ReactionsRowView.tsx rename to packages/shared-components/src/message-body/ReactionsRow/ReactionsRowView.tsx index fffd9d6c1b..379cf58857 100644 --- a/packages/shared-components/src/message-body/ReactionRow/ReactionsRowView.tsx +++ b/packages/shared-components/src/message-body/ReactionsRow/ReactionsRowView.tsx @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -import React, { type JSX, type MouseEventHandler, type ReactNode } from "react"; +import React, { type JSX, type MouseEventHandler, type PropsWithChildren } from "react"; import classNames from "classnames"; import { ReactionAddIcon } from "@vector-im/compound-design-tokens/assets/web/icons"; import { Tooltip } from "@vector-im/compound-web"; @@ -22,14 +22,6 @@ export interface ReactionsRowViewSnapshot { * Controls whether the row should render at all. */ isVisible: boolean; - /** - * Reaction button elements to render in the row. - */ - children?: ReactNode; - /** - * Optional CSS className for the row container. - */ - className?: string; /** * Whether to render the "show all" button. */ @@ -79,14 +71,20 @@ export type ReactionsRowViewModel = ViewModel): JSX.Element { +export function ReactionsRowView({ vm, className, children }: Readonly): JSX.Element { const { ariaLabel, isVisible, - children, - className, showAllButtonVisible, showAllButtonLabel, showAddReactionButton, diff --git a/packages/shared-components/src/message-body/ReactionRow/__snapshots__/ReactionsRow.test.tsx.snap b/packages/shared-components/src/message-body/ReactionsRow/__snapshots__/ReactionsRow.test.tsx.snap similarity index 100% rename from packages/shared-components/src/message-body/ReactionRow/__snapshots__/ReactionsRow.test.tsx.snap rename to packages/shared-components/src/message-body/ReactionsRow/__snapshots__/ReactionsRow.test.tsx.snap diff --git a/packages/shared-components/src/message-body/ReactionRow/index.tsx b/packages/shared-components/src/message-body/ReactionsRow/index.tsx similarity index 100% rename from packages/shared-components/src/message-body/ReactionRow/index.tsx rename to packages/shared-components/src/message-body/ReactionsRow/index.tsx diff --git a/packages/shared-components/src/profile/DisambiguatedProfile/DisambiguatedProfile.stories.tsx b/packages/shared-components/src/profile/DisambiguatedProfile/DisambiguatedProfile.stories.tsx index b3231a5f75..d2409b21d0 100644 --- a/packages/shared-components/src/profile/DisambiguatedProfile/DisambiguatedProfile.stories.tsx +++ b/packages/shared-components/src/profile/DisambiguatedProfile/DisambiguatedProfile.stories.tsx @@ -19,9 +19,13 @@ import { withViewDocs } from "../../../.storybook/withViewDocs"; type DisambiguatedProfileProps = DisambiguatedProfileViewSnapshot & DisambiguatedProfileViewActions; -const DisambiguatedProfileViewWrapperImpl = ({ onClick, ...rest }: DisambiguatedProfileProps): JSX.Element => { +const DisambiguatedProfileViewWrapperImpl = ({ + onClick, + className, + ...rest +}: DisambiguatedProfileProps & { className?: string }): JSX.Element => { const vm = useMockedViewModel(rest, { onClick }); - return ; + return ; }; const DisambiguatedProfileViewWrapper = withViewDocs(DisambiguatedProfileViewWrapperImpl, DisambiguatedProfileView); diff --git a/packages/shared-components/src/profile/DisambiguatedProfile/DisambiguatedProfile.test.tsx b/packages/shared-components/src/profile/DisambiguatedProfile/DisambiguatedProfile.test.tsx index 0b05a1e0ad..a27b879b52 100644 --- a/packages/shared-components/src/profile/DisambiguatedProfile/DisambiguatedProfile.test.tsx +++ b/packages/shared-components/src/profile/DisambiguatedProfile/DisambiguatedProfile.test.tsx @@ -209,4 +209,14 @@ describe("DisambiguatedProfileView", () => { const displayNameElement = screen.getByText("Emphasized User"); expect(displayNameElement).toHaveClass("mx_DisambiguatedProfile_displayName"); }); + + it("should apply custom className to the profile container", () => { + const vm = new DisambiguatedProfileViewModel({ + displayName: "Classed User", + }); + + render(); + const profileContainer = getProfileContainer("Classed User"); + expect(profileContainer).toHaveClass("custom-profile", "another-class"); + }); }); diff --git a/packages/shared-components/src/profile/DisambiguatedProfile/DisambiguatedProfileView.tsx b/packages/shared-components/src/profile/DisambiguatedProfile/DisambiguatedProfileView.tsx index 3489541cfd..81fdbac83b 100644 --- a/packages/shared-components/src/profile/DisambiguatedProfile/DisambiguatedProfileView.tsx +++ b/packages/shared-components/src/profile/DisambiguatedProfile/DisambiguatedProfileView.tsx @@ -24,10 +24,6 @@ export interface DisambiguatedProfileViewSnapshot { * Undefined if coloring is not enabled. */ colorClass?: string; - /** - * The CSS class name. - */ - className?: string; /** * The formatted user identifier to display when disambiguation is needed. * Undefined if disambiguation is not required. @@ -67,6 +63,10 @@ interface DisambiguatedProfileViewProps { * The view model for the disambiguated profile. */ vm: DisambiguatedProfileViewModel; + /** + * Optional CSS class name applied to the profile container. + */ + className?: string; } /** @@ -79,8 +79,8 @@ interface DisambiguatedProfileViewProps { * * ``` */ -export function DisambiguatedProfileView({ vm }: Readonly): JSX.Element { - const { displayName, colorClass, displayIdentifier, title, emphasizeDisplayName, className } = useViewModel(vm); +export function DisambiguatedProfileView({ vm, className }: Readonly): JSX.Element { + const { displayName, colorClass, displayIdentifier, title, emphasizeDisplayName } = useViewModel(vm); const displayNameClasses = classNames(colorClass, { [styles.disambiguatedProfile_displayName]: emphasizeDisplayName, diff --git a/packages/shared-components/src/timeline/DateSeparatorView/DateSeparatorView.stories.tsx b/packages/shared-components/src/timeline/DateSeparatorView/DateSeparatorView.stories.tsx index d09f04d0a6..b7e14bca05 100644 --- a/packages/shared-components/src/timeline/DateSeparatorView/DateSeparatorView.stories.tsx +++ b/packages/shared-components/src/timeline/DateSeparatorView/DateSeparatorView.stories.tsx @@ -20,10 +20,11 @@ const DateSeparatorViewWrapperImpl = ({ onLastMonthPicked, onBeginningPicked, onDatePicked, + className, ...rest -}: DateSeparatorProps): JSX.Element => { +}: DateSeparatorProps & { className?: string }): JSX.Element => { const vm = useMockedViewModel(rest, { onLastWeekPicked, onLastMonthPicked, onBeginningPicked, onDatePicked }); - return ; + return ; }; const DateSeparatorViewWrapper = withViewDocs(DateSeparatorViewWrapperImpl, DateSeparatorView); diff --git a/packages/shared-components/src/timeline/DateSeparatorView/DateSeparatorView.tsx b/packages/shared-components/src/timeline/DateSeparatorView/DateSeparatorView.tsx index a21bed9f82..4d2c21cd96 100644 --- a/packages/shared-components/src/timeline/DateSeparatorView/DateSeparatorView.tsx +++ b/packages/shared-components/src/timeline/DateSeparatorView/DateSeparatorView.tsx @@ -29,10 +29,6 @@ export interface DateSeparatorViewSnapshot { * Reference date as input format used to prefill the jump-to-date picker value. */ jumpFromDate?: string; - /** - * Extra CSS classes to apply to the component. - */ - className?: string; } export interface DateSeparatorViewActions { @@ -56,6 +52,10 @@ interface DateSeparatorViewProps { * The view model for the component. */ vm: DateSeparatorViewModel; + /** + * Extra CSS classes to apply to the component. + */ + className?: string; } /** @@ -68,8 +68,8 @@ interface DateSeparatorViewProps { * * ``` */ -export function DateSeparatorView({ vm }: Readonly): JSX.Element { - const { label, className, jumpToEnabled } = useViewModel(vm); +export function DateSeparatorView({ vm, className }: Readonly): JSX.Element { + const { label, jumpToEnabled } = useViewModel(vm); const [isMenuOpen, setIsMenuOpen] = useState(false); const [isTriggerHovered, setIsTriggerHovered] = useState(false); const [isTriggerFocused, setIsTriggerFocused] = useState(false);