From 0429809c00bee75c255ae981ee7b6e4372ef09c6 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Mon, 2 Dec 2024 19:10:17 +0100 Subject: [PATCH] New UX for Share dialog (#28598) * New UX for `ShareDialog` * Use new named import * Rewrite tests * Add e2e tests * Use `box-sizing` for social buttons * Update e2e tests --- .../e2e/share-dialog/share-dialog.spec.ts | 67 ++ .../share-dialog-event-linux.png | Bin 0 -> 17837 bytes .../share-dialog-room-linux.png | Bin 0 -> 15840 bytes .../share-dialog-user-linux.png | Bin 0 -> 16839 bytes res/css/_common.pcss | 14 +- res/css/views/dialogs/_ShareDialog.pcss | 97 +- .../context_menus/MessageContextMenu.tsx | 2 +- src/components/views/dialogs/ShareDialog.tsx | 296 +++--- .../views/right_panel/RoomSummaryCard.tsx | 2 +- src/components/views/right_panel/UserInfo.tsx | 2 +- .../rooms/RoomHeader/CallGuestLinkButton.tsx | 2 +- src/i18n/strings/en_EN.json | 2 +- .../views/dialogs/ShareDialog-test.tsx | 176 ++-- .../__snapshots__/ShareDialog-test.tsx.snap | 852 ++++++++++++++++++ .../right_panel/RoomSummaryCard-test.tsx | 2 +- .../views/right_panel/UserInfo-test.tsx | 2 +- .../RoomHeader/CallGuestLinkButton-test.tsx | 2 +- 17 files changed, 1240 insertions(+), 278 deletions(-) create mode 100644 playwright/e2e/share-dialog/share-dialog.spec.ts create mode 100644 playwright/snapshots/share-dialog/share-dialog.spec.ts/share-dialog-event-linux.png create mode 100644 playwright/snapshots/share-dialog/share-dialog.spec.ts/share-dialog-room-linux.png create mode 100644 playwright/snapshots/share-dialog/share-dialog.spec.ts/share-dialog-user-linux.png create mode 100644 test/unit-tests/components/views/dialogs/__snapshots__/ShareDialog-test.tsx.snap diff --git a/playwright/e2e/share-dialog/share-dialog.spec.ts b/playwright/e2e/share-dialog/share-dialog.spec.ts new file mode 100644 index 00000000000..2999b74ca03 --- /dev/null +++ b/playwright/e2e/share-dialog/share-dialog.spec.ts @@ -0,0 +1,67 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only + * Please see LICENSE files in the repository root for full details. + */ + +import { test, expect } from "../../element-web-test"; + +test.describe("Share dialog", () => { + test.use({ + displayName: "Alice", + room: async ({ app, user, bot }, use) => { + const roomId = await app.client.createRoom({ name: "Alice room" }); + await use({ roomId }); + }, + }); + + test("should share a room", async ({ page, app, room }) => { + await app.viewRoomById(room.roomId); + await app.toggleRoomInfoPanel(); + await page.getByRole("menuitem", { name: "Copy link" }).click(); + + const dialog = page.getByRole("dialog", { name: "Share room" }); + await expect(dialog.getByText(`https://matrix.to/#/${room.roomId}`)).toBeVisible(); + expect(dialog).toMatchScreenshot("share-dialog-room.png", { + // QRCode and url changes at every run + mask: [page.locator(".mx_QRCode"), page.locator(".mx_ShareDialog_top > span")], + }); + }); + + test("should share a room member", async ({ page, app, room, user }) => { + await app.viewRoomById(room.roomId); + await app.client.sendMessage(room.roomId, { body: "hello", msgtype: "m.text" }); + + const rightPanel = await app.toggleRoomInfoPanel(); + await rightPanel.getByRole("menuitem", { name: "People" }).click(); + await rightPanel.getByRole("button", { name: `${user.userId} (power 100)` }).click(); + await rightPanel.getByRole("button", { name: "Share profile" }).click(); + + const dialog = page.getByRole("dialog", { name: "Share User" }); + await expect(dialog.getByText(`https://matrix.to/#/${user.userId}`)).toBeVisible(); + expect(dialog).toMatchScreenshot("share-dialog-user.png", { + // QRCode changes at every run + mask: [page.locator(".mx_QRCode")], + }); + }); + + test("should share an event", async ({ page, app, room }) => { + await app.viewRoomById(room.roomId); + await app.client.sendMessage(room.roomId, { body: "hello", msgtype: "m.text" }); + + const timelineMessage = page.locator(".mx_MTextBody", { hasText: "hello" }); + await timelineMessage.hover(); + await page.getByRole("button", { name: "Options", exact: true }).click(); + await page.getByRole("menuitem", { name: "Share" }).click(); + + const dialog = page.getByRole("dialog", { name: "Share Room Message" }); + await expect(dialog.getByRole("checkbox", { name: "Link to selected message" })).toBeChecked(); + expect(dialog).toMatchScreenshot("share-dialog-event.png", { + // QRCode and url changes at every run + mask: [page.locator(".mx_QRCode"), page.locator(".mx_ShareDialog_top > span")], + }); + await dialog.getByRole("checkbox", { name: "Link to selected message" }).click(); + await expect(dialog.getByRole("checkbox", { name: "Link to selected message" })).not.toBeChecked(); + }); +}); diff --git a/playwright/snapshots/share-dialog/share-dialog.spec.ts/share-dialog-event-linux.png b/playwright/snapshots/share-dialog/share-dialog.spec.ts/share-dialog-event-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..2f703cfc8ac4690eebf48176556e96371eb773fa GIT binary patch literal 17837 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 B;y?fZ literal 0 HcmV?d00001 diff --git a/playwright/snapshots/share-dialog/share-dialog.spec.ts/share-dialog-room-linux.png b/playwright/snapshots/share-dialog/share-dialog.spec.ts/share-dialog-room-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..725e095f6f9d29c047ebbee53dc065933ca80b42 GIT binary patch literal 15840 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 H>-YZysD(JC literal 0 HcmV?d00001 diff --git a/res/css/_common.pcss b/res/css/_common.pcss index 15ba02b6b88..74328af39b2 100644 --- a/res/css/_common.pcss +++ b/res/css/_common.pcss @@ -596,7 +596,7 @@ legend { .mx_Dialog button:not(.mx_Dialog_nonDialogButton):not([class|="maplibregl"]):not(.mx_AccessibleButton):not( .mx_UserProfileSettings button - ):not(.mx_ThemeChoicePanel_CustomTheme button):not(.mx_UnpinAllDialog button), + ):not(.mx_ThemeChoicePanel_CustomTheme button):not(.mx_UnpinAllDialog button):not(.mx_ShareDialog button), .mx_Dialog input[type="submit"], .mx_Dialog_buttons button:not(.mx_Dialog_nonDialogButton):not(.mx_AccessibleButton), .mx_Dialog_buttons input[type="submit"] { @@ -616,14 +616,16 @@ legend { .mx_Dialog button:not(.mx_Dialog_nonDialogButton):not([class|="maplibregl"]):not(.mx_AccessibleButton):not( .mx_UserProfileSettings button - ):not(.mx_ThemeChoicePanel_CustomTheme button):not(.mx_UnpinAllDialog button):last-child { + ):not(.mx_ThemeChoicePanel_CustomTheme button):not(.mx_UnpinAllDialog button):not( + .mx_ShareDialog button + ):last-child { margin-right: 0px; } .mx_Dialog button:not(.mx_Dialog_nonDialogButton):not([class|="maplibregl"]):not(.mx_AccessibleButton):not( .mx_UserProfileSettings button - ):not(.mx_ThemeChoicePanel_CustomTheme button):not(.mx_UnpinAllDialog button):focus, + ):not(.mx_ThemeChoicePanel_CustomTheme button):not(.mx_UnpinAllDialog button):not(.mx_ShareDialog button):focus, .mx_Dialog input[type="submit"]:focus, .mx_Dialog_buttons button:not(.mx_Dialog_nonDialogButton):not(.mx_AccessibleButton):focus, .mx_Dialog_buttons input[type="submit"]:focus { @@ -635,7 +637,7 @@ legend { .mx_Dialog_buttons button.mx_Dialog_primary:not(.mx_Dialog_nonDialogButton):not(.mx_AccessibleButton):not( .mx_UserProfileSettings button - ):not(.mx_ThemeChoicePanel_CustomTheme button):not(.mx_UnpinAllDialog button), + ):not(.mx_ThemeChoicePanel_CustomTheme button):not(.mx_UnpinAllDialog button):not(.mx_ShareDialog button), .mx_Dialog_buttons input[type="submit"].mx_Dialog_primary { color: var(--cpd-color-text-on-solid-primary); background-color: var(--cpd-color-bg-action-primary-rest); @@ -648,7 +650,7 @@ legend { .mx_Dialog_buttons button.danger:not(.mx_Dialog_nonDialogButton):not(.mx_AccessibleButton):not(.mx_UserProfileSettings button):not( .mx_ThemeChoicePanel_CustomTheme button - ):not(.mx_UnpinAllDialog button), + ):not(.mx_UnpinAllDialog button):not(.mx_ShareDialog button), .mx_Dialog_buttons input[type="submit"].danger { background-color: var(--cpd-color-bg-critical-primary); border: solid 1px var(--cpd-color-bg-critical-primary); @@ -664,7 +666,7 @@ legend { .mx_Dialog button:not(.mx_Dialog_nonDialogButton):not([class|="maplibregl"]):not(.mx_AccessibleButton):not( .mx_UserProfileSettings button - ):not(.mx_ThemeChoicePanel_CustomTheme button):not(.mx_UnpinAllDialog button):disabled, + ):not(.mx_ThemeChoicePanel_CustomTheme button):not(.mx_UnpinAllDialog button):not(.mx_ShareDialog button):disabled, .mx_Dialog input[type="submit"]:disabled, .mx_Dialog_buttons button:not(.mx_Dialog_nonDialogButton):not(.mx_AccessibleButton):disabled, .mx_Dialog_buttons input[type="submit"]:disabled { diff --git a/res/css/views/dialogs/_ShareDialog.pcss b/res/css/views/dialogs/_ShareDialog.pcss index 086222af31b..cfede43aae7 100644 --- a/res/css/views/dialogs/_ShareDialog.pcss +++ b/res/css/views/dialogs/_ShareDialog.pcss @@ -5,50 +5,73 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only Please see LICENSE files in the repository root for full details. */ -.mx_ShareDialog hr { - margin-top: 25px; - margin-bottom: 25px; - border-color: $light-fg-color; -} +.mx_ShareDialog { + /* Value from figma design */ + width: 416px; + + .mx_Dialog_header { + text-align: center; + margin-bottom: var(--cpd-space-6x); + /* Override dialog header padding to able to center it */ + padding-inline-end: 0; + } -.mx_ShareDialog .mx_ShareDialog_content { - margin: 10px 0; + .mx_ShareDialog_content { + display: flex; + flex-direction: column; + gap: var(--cpd-space-6x); + align-items: center; - .mx_CopyableText { - width: unset; /* full width */ + .mx_ShareDialog_top { + display: flex; + flex-direction: column; + gap: var(--cpd-space-4x); + align-items: center; + width: 100%; - > a { - text-decoration: none; - flex-shrink: 1; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; + span { + text-align: center; + font: var(--cpd-font-body-sm-semibold); + color: var(--cpd-color-text-secondary); + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + width: 100%; + } } - } -} -.mx_ShareDialog_split { - display: flex; - flex-wrap: wrap; -} + label { + display: inline-flex; + gap: var(--cpd-space-3x); + justify-content: center; + align-items: center; + font: var(--cpd-font-body-md-medium); + } -.mx_ShareDialog_qrcode_container { - float: left; - height: 256px; - width: 256px; - margin-right: 64px; -} + button { + width: 100%; + } -.mx_ShareDialog_qrcode_container + .mx_ShareDialog_social_container { - width: 299px; -} + .mx_ShareDialog_social { + display: flex; + gap: var(--cpd-space-3x); + justify-content: center; -.mx_ShareDialog_social_container { - display: inline-block; -} + a { + width: 48px; + height: 48px; + border-radius: 99px; + box-sizing: border-box; + border: 1px solid var(--cpd-color-border-interactive-secondary); + display: flex; + justify-content: center; + align-items: center; -.mx_ShareDialog_social_icon { - display: inline-grid; - margin-right: 10px; - margin-bottom: 10px; + img { + width: 24px; + height: 24px; + } + } + } + } } diff --git a/src/components/views/context_menus/MessageContextMenu.tsx b/src/components/views/context_menus/MessageContextMenu.tsx index 2b559aa74c3..711ffbe70f9 100644 --- a/src/components/views/context_menus/MessageContextMenu.tsx +++ b/src/components/views/context_menus/MessageContextMenu.tsx @@ -38,7 +38,7 @@ import ContextMenu, { toRightOf, MenuProps } from "../../structures/ContextMenu" import ReactionPicker from "../emojipicker/ReactionPicker"; import ViewSource from "../../structures/ViewSource"; import { createRedactEventDialog } from "../dialogs/ConfirmRedactDialog"; -import ShareDialog from "../dialogs/ShareDialog"; +import { ShareDialog } from "../dialogs/ShareDialog"; import RoomContext, { TimelineRenderingType } from "../../../contexts/RoomContext"; import EndPollDialog from "../dialogs/EndPollDialog"; import { isPollEnded } from "../messages/MPollBody"; diff --git a/src/components/views/dialogs/ShareDialog.tsx b/src/components/views/dialogs/ShareDialog.tsx index f9382227e4e..1796b79239e 100644 --- a/src/components/views/dialogs/ShareDialog.tsx +++ b/src/components/views/dialogs/ShareDialog.tsx @@ -7,22 +7,23 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only Please see LICENSE files in the repository root for full details. */ -import * as React from "react"; +import React, { JSX, useMemo, useRef, useState } from "react"; import { Room, RoomMember, MatrixEvent, User } from "matrix-js-sdk/src/matrix"; +import { Checkbox, Button } from "@vector-im/compound-web"; +import LinkIcon from "@vector-im/compound-design-tokens/assets/web/icons/link"; +import CheckIcon from "@vector-im/compound-design-tokens/assets/web/icons/check"; import { _t } from "../../../languageHandler"; import QRCode from "../elements/QRCode"; import { RoomPermalinkCreator, makeUserPermalink } from "../../../utils/permalinks/Permalinks"; -import { selectText } from "../../../utils/strings"; -import StyledCheckbox from "../elements/StyledCheckbox"; -import SettingsStore from "../../../settings/SettingsStore"; +import { copyPlaintext } from "../../../utils/strings"; import { UIFeature } from "../../../settings/UIFeature"; import BaseDialog from "./BaseDialog"; -import CopyableText from "../elements/CopyableText"; import { XOR } from "../../../@types/common"; +import { useSettingValue } from "../../../hooks/useSettings.ts"; /* eslint-disable @typescript-eslint/no-require-imports */ -const socials = [ +const SOCIALS = [ { name: "Facebook", img: require("../../../../res/img/social/facebook.png"), @@ -33,11 +34,7 @@ const socials = [ img: require("../../../../res/img/social/twitter-2.png"), url: (url: string) => `https://twitter.com/home?status=${url}`, }, - /* // icon missing - name: 'Google Plus', - img: 'img/social/', - url: (url) => `https://plus.google.com/share?url=${url}`, - },*/ { + { name: "LinkedIn", img: require("../../../../res/img/social/linkedin.png"), url: (url: string) => `https://www.linkedin.com/shareArticle?mini=true&url=${url}`, @@ -78,160 +75,153 @@ interface Props extends BaseProps { * A matrix.to link will be generated out of it if it's not already a url. */ target: Room | User | RoomMember | URL; + /** + * Optional when the target is a Room, User, RoomMember or a URL. + * Mandatory when the target is a MatrixEvent. + */ permalinkCreator?: RoomPermalinkCreator; } interface EventProps extends BaseProps { + /** + * The target to link to. + */ target: MatrixEvent; + /** + * Optional when the target is a Room, User, RoomMember or a URL. + * Mandatory when the target is a MatrixEvent. + */ permalinkCreator: RoomPermalinkCreator; } -interface IState { - linkSpecificEvent: boolean; - permalinkCreator: RoomPermalinkCreator | null; +type ShareDialogProps = XOR; + +/** + * A dialog to share a link to a room, user, room member or a matrix event. + */ +export function ShareDialog({ target, customTitle, onFinished, permalinkCreator }: ShareDialogProps): JSX.Element { + const showQrCode = useSettingValue(UIFeature.ShareQRCode); + const showSocials = useSettingValue(UIFeature.ShareSocial); + + const timeoutIdRef = useRef(); + const [isCopied, setIsCopied] = useState(false); + + const [linkToSpecificEvent, setLinkToSpecificEvent] = useState(target instanceof MatrixEvent); + const { title, url, checkboxLabel } = useTargetValues(target, linkToSpecificEvent, permalinkCreator); + const newTitle = customTitle ?? title; + + return ( + +
+
+ {showQrCode && } + {url} +
+ {checkboxLabel && ( + + )} + + {showSocials && } +
+
+ ); } -export default class ShareDialog extends React.PureComponent, IState> { - public constructor(props: XOR) { - super(props); - - let permalinkCreator: RoomPermalinkCreator | null = null; - if (props.target instanceof Room) { - permalinkCreator = new RoomPermalinkCreator(props.target); - permalinkCreator.load(); - } +/** + * Social links to share the link on different platforms. + */ +interface SocialLinksProps { + /** + * The URL to share. + */ + url: string; +} - this.state = { - // MatrixEvent defaults to share linkSpecificEvent - linkSpecificEvent: this.props.target instanceof MatrixEvent, - permalinkCreator, - }; - } - - public static onLinkClick(e: React.MouseEvent): void { - e.preventDefault(); - selectText(e.currentTarget); - } - - private onLinkSpecificEventCheckboxClick = (): void => { - this.setState({ - linkSpecificEvent: !this.state.linkSpecificEvent, - }); - }; - - private getUrl(): string { - if (this.props.target instanceof URL) { - return this.props.target.toString(); - } else if (this.props.target instanceof Room) { - if (this.state.linkSpecificEvent) { - const events = this.props.target.getLiveTimeline().getEvents(); - return this.state.permalinkCreator!.forEvent(events[events.length - 1].getId()!); - } else { - return this.state.permalinkCreator!.forShareableRoom(); - } - } else if (this.props.target instanceof User || this.props.target instanceof RoomMember) { - return makeUserPermalink(this.props.target.userId); - } else if (this.state.linkSpecificEvent) { - return this.props.permalinkCreator!.forEvent(this.props.target.getId()!); - } else { - return this.props.permalinkCreator!.forShareableRoom(); - } - } - - public render(): React.ReactNode { - let title: string | undefined; - let checkbox: JSX.Element | undefined; - - if (this.props.target instanceof URL) { - title = this.props.customTitle ?? _t("share|title_link"); - } else if (this.props.target instanceof Room) { - title = this.props.customTitle ?? _t("share|title_room"); - - const events = this.props.target.getLiveTimeline().getEvents(); - if (events.length > 0) { - checkbox = ( -
- - {_t("share|permalink_most_recent")} - -
- ); - } - } else if (this.props.target instanceof User || this.props.target instanceof RoomMember) { - title = this.props.customTitle ?? _t("share|title_user"); - } else if (this.props.target instanceof MatrixEvent) { - title = this.props.customTitle ?? _t("share|title_message"); - checkbox = ( -
- - {_t("share|permalink_message")} - -
- ); - } +/** + * The socials to share the link on. + */ +function SocialLinks({ url }: SocialLinksProps): JSX.Element { + return ( +
+ ); +} - const matrixToUrl = this.getUrl(); - const encodedUrl = encodeURIComponent(matrixToUrl); - - const showQrCode = SettingsStore.getValue(UIFeature.ShareQRCode); - const showSocials = SettingsStore.getValue(UIFeature.ShareSocial); - - let qrSocialSection; - if (showQrCode || showSocials) { - qrSocialSection = ( - <> -
-
- {showQrCode && ( -
- -
- )} - {showSocials && ( -
- {socials.map((social) => ( - - {social.name} - - ))} -
- )} -
- - ); +/** + * Get the title, url and checkbox label for the dialog based on the target. + * @param target + * @param linkToSpecificEvent + * @param permalinkCreator + */ +function useTargetValues( + target: ShareDialogProps["target"], + linkToSpecificEvent: boolean, + permalinkCreator?: RoomPermalinkCreator, +): { title: string; url: string; checkboxLabel?: string } { + return useMemo(() => { + if (target instanceof URL) return { title: _t("share|title_link"), url: target.toString() }; + if (target instanceof User || target instanceof RoomMember) + return { + title: _t("share|title_user"), + url: makeUserPermalink(target.userId), + }; + + if (target instanceof Room) { + const title = _t("share|title_room"); + const newPermalinkCreator = new RoomPermalinkCreator(target); + newPermalinkCreator.load(); + + const events = target.getLiveTimeline().getEvents(); + return { + title, + url: linkToSpecificEvent + ? newPermalinkCreator.forEvent(events[events.length - 1].getId()!) + : newPermalinkCreator.forShareableRoom(), + ...(events.length > 0 && { checkboxLabel: _t("share|permalink_most_recent") }), + }; } - return ( - - {this.props.subtitle &&

{this.props.subtitle}

} -
- matrixToUrl}> - - {matrixToUrl} - - - {checkbox} - {qrSocialSection} -
-
- ); - } + // MatrixEvent is remaining and should have a permalinkCreator + const url = linkToSpecificEvent + ? permalinkCreator!.forEvent(target.getId()!) + : permalinkCreator!.forShareableRoom(); + return { + title: _t("share|title_message"), + url, + checkboxLabel: _t("share|permalink_message"), + }; + }, [target, linkToSpecificEvent, permalinkCreator]); } diff --git a/src/components/views/right_panel/RoomSummaryCard.tsx b/src/components/views/right_panel/RoomSummaryCard.tsx index d0e3694ea59..c8dd0b97387 100644 --- a/src/components/views/right_panel/RoomSummaryCard.tsx +++ b/src/components/views/right_panel/RoomSummaryCard.tsx @@ -47,7 +47,7 @@ import RoomAvatar from "../avatars/RoomAvatar"; import defaultDispatcher from "../../../dispatcher/dispatcher"; import { RightPanelPhases } from "../../../stores/right-panel/RightPanelStorePhases"; import Modal from "../../../Modal"; -import ShareDialog from "../dialogs/ShareDialog"; +import { ShareDialog } from "../dialogs/ShareDialog"; import { useEventEmitterState } from "../../../hooks/useEventEmitter"; import { E2EStatus } from "../../../utils/ShieldUtils"; import { RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks"; diff --git a/src/components/views/right_panel/UserInfo.tsx b/src/components/views/right_panel/UserInfo.tsx index b4a775367ce..591e2327ae4 100644 --- a/src/components/views/right_panel/UserInfo.tsx +++ b/src/components/views/right_panel/UserInfo.tsx @@ -63,7 +63,7 @@ import PowerSelector from "../elements/PowerSelector"; import MemberAvatar from "../avatars/MemberAvatar"; import PresenceLabel from "../rooms/PresenceLabel"; import BulkRedactDialog from "../dialogs/BulkRedactDialog"; -import ShareDialog from "../dialogs/ShareDialog"; +import { ShareDialog } from "../dialogs/ShareDialog"; import ErrorDialog from "../dialogs/ErrorDialog"; import QuestionDialog from "../dialogs/QuestionDialog"; import ConfirmUserActionDialog from "../dialogs/ConfirmUserActionDialog"; diff --git a/src/components/views/rooms/RoomHeader/CallGuestLinkButton.tsx b/src/components/views/rooms/RoomHeader/CallGuestLinkButton.tsx index ae8e7be16bf..8c000bdf3bc 100644 --- a/src/components/views/rooms/RoomHeader/CallGuestLinkButton.tsx +++ b/src/components/views/rooms/RoomHeader/CallGuestLinkButton.tsx @@ -12,7 +12,7 @@ import { logger } from "matrix-js-sdk/src/logger"; import { EventType, JoinRule, Room } from "matrix-js-sdk/src/matrix"; import Modal from "../../../../Modal"; -import ShareDialog from "../../dialogs/ShareDialog"; +import { ShareDialog } from "../../dialogs/ShareDialog"; import { _t } from "../../../../languageHandler"; import SettingsStore from "../../../../settings/SettingsStore"; import { calculateRoomVia } from "../../../../utils/permalinks/Permalinks"; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 31a6c71c1ac..e601a7ecd53 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2944,7 +2944,7 @@ "warning": "WARNING: " }, "share": { - "link_title": "Link to room", + "link_copied": "Link copied", "permalink_message": "Link to selected message", "permalink_most_recent": "Link to most recent message", "share_call": "Conference invite link", diff --git a/test/unit-tests/components/views/dialogs/ShareDialog-test.tsx b/test/unit-tests/components/views/dialogs/ShareDialog-test.tsx index cb7d556235c..c1d9883b7f6 100644 --- a/test/unit-tests/components/views/dialogs/ShareDialog-test.tsx +++ b/test/unit-tests/components/views/dialogs/ShareDialog-test.tsx @@ -7,111 +7,139 @@ Please see LICENSE files in the repository root for full details. */ import React from "react"; -import { EventTimeline, MatrixEvent, Room, RoomMember } from "matrix-js-sdk/src/matrix"; -import { render, RenderOptions } from "jest-matrix-react"; +import { MatrixClient, MatrixEvent, Room, RoomMember } from "matrix-js-sdk/src/matrix"; +import { render, screen, act } from "jest-matrix-react"; +import userEvent from "@testing-library/user-event"; +import { waitFor } from "@testing-library/dom"; -import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg"; import SettingsStore from "../../../../../src/settings/SettingsStore"; -import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext"; -import { _t } from "../../../../../src/languageHandler"; -import ShareDialog from "../../../../../src/components/views/dialogs/ShareDialog"; +import { ShareDialog } from "../../../../../src/components/views/dialogs/ShareDialog"; import { UIFeature } from "../../../../../src/settings/UIFeature"; -import { stubClient } from "../../../../test-utils"; -jest.mock("../../../../../src/utils/ShieldUtils"); - -function getWrapper(): RenderOptions { - return { - wrapper: ({ children }) => ( - {children} - ), - }; -} +import { stubClient, withClientContextRenderOptions } from "../../../../test-utils"; +import * as StringsModule from "../../../../../src/utils/strings"; +import { RoomPermalinkCreator } from "../../../../../src/utils/permalinks/Permalinks.ts"; describe("ShareDialog", () => { + let client: MatrixClient; let room: Room; - - const ROOM_ID = "!1:example.org"; + const copyTextFunc = jest.fn(); beforeEach(async () => { - stubClient(); - room = new Room(ROOM_ID, MatrixClientPeg.get()!, "@alice:example.org"); + client = stubClient(); + room = new Room("!1:example.org", client, "@alice:example.org"); + jest.spyOn(StringsModule, "copyPlaintext").mockImplementation(copyTextFunc); }); afterEach(() => { jest.restoreAllMocks(); + copyTextFunc.mockClear(); }); - it("renders room share dialog", () => { - const { container: withoutEvents } = render(, getWrapper()); - expect(withoutEvents).toHaveTextContent(_t("share|title_room")); + function renderComponent(target: Room | RoomMember | URL) { + return render(, withClientContextRenderOptions(client)); + } + + const getUrl = () => new URL("https://matrix.org/"); + const getRoomMember = () => new RoomMember(room.roomId, "@alice:example.org"); + + test.each([ + { name: "an URL", title: "Share Link", url: "https://matrix.org/", getTarget: getUrl }, + { + name: "a room member", + title: "Share User", + url: "https://matrix.to/#/@alice:example.org", + getTarget: getRoomMember, + }, + ])("should render a share dialog for $name", async ({ title, url, getTarget }) => { + const { asFragment } = renderComponent(getTarget()); + + expect(screen.getByRole("heading", { name: title })).toBeInTheDocument(); + expect(screen.getByText(url)).toBeInTheDocument(); + expect(asFragment()).toMatchSnapshot(); - jest.spyOn(room, "getLiveTimeline").mockReturnValue({ getEvents: () => [{} as MatrixEvent] } as EventTimeline); - const { container: withEvents } = render(, getWrapper()); - expect(withEvents).toHaveTextContent(_t("share|permalink_most_recent")); + await userEvent.click(screen.getByRole("button", { name: "Copy link" })); + expect(copyTextFunc).toHaveBeenCalledWith(url); }); - it("renders user share dialog", () => { - mockRoomMembers(room, 1); - const { container } = render( - , - getWrapper(), - ); - expect(container).toHaveTextContent(_t("share|title_user")); + it("should render a share dialog for a room", async () => { + const expectedURL = "https://matrix.to/#/!1:example.org"; + jest.spyOn(room.getLiveTimeline(), "getEvents").mockReturnValue([new MatrixEvent({ event_id: "!eventId" })]); + + const { asFragment } = renderComponent(room); + expect(screen.getByRole("heading", { name: "Share Room" })).toBeInTheDocument(); + expect(screen.getByText(expectedURL)).toBeInTheDocument(); + expect(screen.getByRole("checkbox", { name: "Link to most recent message" })).toBeInTheDocument(); + expect(asFragment()).toMatchSnapshot(); + + await userEvent.click(screen.getByRole("button", { name: "Copy link" })); + expect(copyTextFunc).toHaveBeenCalledWith(expectedURL); + + // Click on the checkbox to link to the most recent message + await userEvent.click(screen.getByRole("checkbox", { name: "Link to most recent message" })); + const newExpectedURL = "https://matrix.to/#/!1:example.org/!eventId"; + expect(screen.getByText(newExpectedURL)).toBeInTheDocument(); }); - it("renders link share dialog", () => { - mockRoomMembers(room, 1); - const { container } = render( - , - getWrapper(), + it("should render a share dialog for a matrix event", async () => { + const matrixEvent = new MatrixEvent({ event_id: "!eventId" }); + const permalinkCreator = new RoomPermalinkCreator(room); + const expectedURL = "https://matrix.to/#/!1:example.org/!eventId"; + + const { asFragment } = render( + , + withClientContextRenderOptions(client), ); - expect(container).toHaveTextContent(_t("share|title_link")); + expect(screen.getByRole("heading", { name: "Share Room Message" })).toBeInTheDocument(); + expect(screen.getByText(expectedURL)).toBeInTheDocument(); + expect(screen.getByRole("checkbox", { name: "Link to selected message" })).toBeChecked(); + expect(asFragment()).toMatchSnapshot(); + + await userEvent.click(screen.getByRole("button", { name: "Copy link" })); + expect(copyTextFunc).toHaveBeenCalledWith(expectedURL); + + // Click on the checkbox to link to the room + await userEvent.click(screen.getByRole("checkbox", { name: "Link to selected message" })); + expect(screen.getByText("https://matrix.to/#/!1:example.org")).toBeInTheDocument(); + }); + + it("should change the copy button text when clicked", async () => { + jest.useFakeTimers(); + const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime }); + // To not be bother with rtl warnings about QR code state update + jest.spyOn(SettingsStore, "getValue").mockReturnValue(false); + + renderComponent(room); + await user.click(screen.getByRole("button", { name: "Copy link" })); + // Move after `copyPlaintext` + await jest.advanceTimersToNextTimerAsync(); + expect(screen.getByRole("button", { name: "Link copied" })).toBeInTheDocument(); + + // 2 sec after the button should be back to normal + act(() => jest.advanceTimersByTime(2000)); + await waitFor(() => expect(screen.getByRole("button", { name: "Copy link" })).toBeInTheDocument()); }); - it("renders the QR code if configured", () => { + it("should not render the QR code if disabled", () => { const originalGetValue = SettingsStore.getValue; jest.spyOn(SettingsStore, "getValue").mockImplementation((feature) => { - if (feature === UIFeature.ShareQRCode) return true; + if (feature === UIFeature.ShareQRCode) return false; return originalGetValue(feature); }); - const { container } = render(, getWrapper()); - const qrCodesVisible = container.getElementsByClassName("mx_ShareDialog_qrcode_container").length > 0; - expect(qrCodesVisible).toBe(true); + + const { asFragment } = renderComponent(room); + expect(screen.queryByRole("img", { name: "QR code" })).toBeNull(); + expect(asFragment()).toMatchSnapshot(); }); - it("renders the social button if configured", () => { + it("should not render the socials if disabled", () => { const originalGetValue = SettingsStore.getValue; jest.spyOn(SettingsStore, "getValue").mockImplementation((feature) => { - if (feature === UIFeature.ShareSocial) return true; + if (feature === UIFeature.ShareSocial) return false; return originalGetValue(feature); }); - const { container } = render(, getWrapper()); - const qrCodesVisible = container.getElementsByClassName("mx_ShareDialog_social_container").length > 0; - expect(qrCodesVisible).toBe(true); - }); - it("renders custom title and subtitle", () => { - const { container } = render( - , - getWrapper(), - ); - expect(container).toHaveTextContent("test_title_123"); - expect(container).toHaveTextContent("custom_subtitle_1234"); + + const { asFragment } = renderComponent(room); + expect(screen.queryByRole("link", { name: "Reddit" })).toBeNull(); + expect(asFragment()).toMatchSnapshot(); }); }); -/** - * - * @param count the number of users to create - */ -function mockRoomMembers(room: Room, count: number) { - const members = Array(count) - .fill(0) - .map((_, index) => new RoomMember(room.roomId, "@alice:example.org")); - - room.currentState.setJoinedMemberCount(members.length); - room.getJoinedMembers = jest.fn().mockReturnValue(members); -} diff --git a/test/unit-tests/components/views/dialogs/__snapshots__/ShareDialog-test.tsx.snap b/test/unit-tests/components/views/dialogs/__snapshots__/ShareDialog-test.tsx.snap new file mode 100644 index 00000000000..ab8b8ffb582 --- /dev/null +++ b/test/unit-tests/components/views/dialogs/__snapshots__/ShareDialog-test.tsx.snap @@ -0,0 +1,852 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ShareDialog should not render the QR code if disabled 1`] = ` + +
+