diff --git a/package.json b/package.json index e483fb3948..a54fa74797 100644 --- a/package.json +++ b/package.json @@ -81,7 +81,7 @@ "@sentry/browser": "^8.0.0", "@testing-library/react-hooks": "^8.0.1", "@vector-im/compound-design-tokens": "^1.2.0", - "@vector-im/compound-web": "^5.2.3", + "@vector-im/compound-web": "^5.4.0", "@zxcvbn-ts/core": "^3.0.4", "@zxcvbn-ts/language-common": "^3.0.4", "@zxcvbn-ts/language-en": "^3.0.2", diff --git a/playwright/e2e/crypto/crypto.spec.ts b/playwright/e2e/crypto/crypto.spec.ts index 30301270ac..1e1015cd53 100644 --- a/playwright/e2e/crypto/crypto.spec.ts +++ b/playwright/e2e/crypto/crypto.spec.ts @@ -227,7 +227,7 @@ test.describe("Cryptography", function () { await verify(page, bob); // Assert that verified icon is rendered - await page.getByRole("button", { name: "Room members" }).click(); + await page.getByTestId("base-card-back-button").click(); await page.locator(".mx_RightPanelTabs").getByText("Info").click(); await expect(page.locator('.mx_RoomSummaryCard_badges [data-kind="success"]')).toContainText("Encrypted"); diff --git a/playwright/e2e/crypto/event-shields.spec.ts b/playwright/e2e/crypto/event-shields.spec.ts index 0de678c17c..d73002263f 100644 --- a/playwright/e2e/crypto/event-shields.spec.ts +++ b/playwright/e2e/crypto/event-shields.spec.ts @@ -132,7 +132,7 @@ test.describe("Cryptography", function () { // wait for the logout to propagate. Workaround for https://github.com/vector-im/element-web/issues/26263 by repeatedly closing and reopening Bob's user info. async function awaitOneDevice(iterations = 1) { const rightPanel = page.locator(".mx_RightPanel"); - await rightPanel.getByRole("button", { name: "Room members" }).click(); + await rightPanel.getByTestId("base-card-back-button").click(); await rightPanel.getByText("Bob").click(); const sessionCountText = await rightPanel .locator(".mx_UserInfo_devices") diff --git a/playwright/e2e/read-receipts/index.ts b/playwright/e2e/read-receipts/index.ts index 484df2251d..a3b45c4190 100644 --- a/playwright/e2e/read-receipts/index.ts +++ b/playwright/e2e/read-receipts/index.ts @@ -538,7 +538,7 @@ class Helpers { const threadPanel = this.page.locator(".mx_ThreadPanel"); await expect(threadPanel).toBeVisible(); await threadPanel.evaluate(($panel) => { - const $button = $panel.querySelector('.mx_BaseCard_back[aria-label="Threads"]'); + const $button = $panel.querySelector('[data-testid="base-card-back-button"]'); // If the Threads back button is present then click it - the // threads button can open either threads list or thread panel if ($button) { diff --git a/playwright/e2e/right-panel/right-panel.spec.ts b/playwright/e2e/right-panel/right-panel.spec.ts index e323a4b24f..a5b6e9ec20 100644 --- a/playwright/e2e/right-panel/right-panel.spec.ts +++ b/playwright/e2e/right-panel/right-panel.spec.ts @@ -106,7 +106,7 @@ test.describe("RightPanel", () => { await expect(page.locator(".mx_FilePanel")).toBeVisible(); await expect(page.locator(".mx_FilePanel_empty")).toBeVisible(); - await page.getByRole("button", { name: "Room information" }).click(); + await page.getByTestId("base-card-back-button").click(); await checkRoomSummaryCard(page, ROOM_NAME); }); @@ -120,7 +120,7 @@ test.describe("RightPanel", () => { await expect(page.locator(".mx_UserInfo")).toBeVisible(); await expect(page.locator(".mx_UserInfo_profile").getByText(NAME)).toBeVisible(); - await page.getByRole("button", { name: "Room members" }).click(); + await page.getByTestId("base-card-back-button").click(); await expect(page.locator(".mx_MemberList")).toBeVisible(); await page.locator(".mx_RightPanelTabs").getByText("Info").click(); @@ -145,7 +145,7 @@ test.describe("RightPanel", () => { await expect(page.locator(".mx_UserInfo_profile").getByText(NAME)).toBeVisible(); await expect(page.locator(".mx_SpaceScopeHeader").getByText(SPACE_NAME)).toBeVisible(); - await page.getByRole("button", { name: "Back" }).click(); + await page.getByTestId("base-card-back-button").click(); await expect(page.locator(".mx_MemberList")).toBeVisible(); }); }); diff --git a/playwright/e2e/threads/threads.spec.ts b/playwright/e2e/threads/threads.spec.ts index 9b5ea46511..d53604aa4c 100644 --- a/playwright/e2e/threads/threads.spec.ts +++ b/playwright/e2e/threads/threads.spec.ts @@ -433,7 +433,7 @@ test.describe("Threads", () => { await textbox.press("Enter"); await expect(locator.locator(".mx_EventTile_last").getByText("Hello Mr. User")).toBeAttached(); // Close thread - await locator.getByRole("button", { name: "Close" }).click(); + await locator.getByTestId("base-card-close-button").click(); // Open existing thread locator = page @@ -486,7 +486,7 @@ test.describe("Threads", () => { await textbox.press("Enter"); await expect(threadPanel.locator(".mx_EventTile_last").getByText(threadMessage)).toBeVisible(); // Close thread - await threadPanel.getByRole("button", { name: "Close" }).click(); + await threadPanel.getByTestId("base-card-close-button").click(); }; await sendMessage("Hello Mr. Bot"); @@ -502,7 +502,7 @@ test.describe("Threads", () => { ).toBeVisible(); // Open threads list - await page.locator(".mx_BaseCard_back").click(); + await page.getByTestId("base-card-back-button").click(); const rightPanel = page.locator(".mx_RightPanel"); // Check that the threads are listed await expect(rightPanel.locator(".mx_EventTile").getByText("Hello Mr. User in a thread")).toBeVisible(); diff --git a/playwright/snapshots/polls/polls.spec.ts/ThreadView-with-a-poll-on-bubble-layout-linux.png b/playwright/snapshots/polls/polls.spec.ts/ThreadView-with-a-poll-on-bubble-layout-linux.png index c85c583a19..9992923226 100644 Binary files a/playwright/snapshots/polls/polls.spec.ts/ThreadView-with-a-poll-on-bubble-layout-linux.png and b/playwright/snapshots/polls/polls.spec.ts/ThreadView-with-a-poll-on-bubble-layout-linux.png differ diff --git a/playwright/snapshots/polls/polls.spec.ts/ThreadView-with-a-poll-on-group-layout-linux.png b/playwright/snapshots/polls/polls.spec.ts/ThreadView-with-a-poll-on-group-layout-linux.png index b6990e727e..050a82a8af 100644 Binary files a/playwright/snapshots/polls/polls.spec.ts/ThreadView-with-a-poll-on-group-layout-linux.png and b/playwright/snapshots/polls/polls.spec.ts/ThreadView-with-a-poll-on-group-layout-linux.png differ diff --git a/playwright/snapshots/right-panel/file-panel.spec.ts/empty-linux.png b/playwright/snapshots/right-panel/file-panel.spec.ts/empty-linux.png index e4f6313c97..fb6d85ae52 100644 Binary files a/playwright/snapshots/right-panel/file-panel.spec.ts/empty-linux.png and b/playwright/snapshots/right-panel/file-panel.spec.ts/empty-linux.png differ diff --git a/playwright/snapshots/right-panel/notification-panel.spec.ts/empty-linux.png b/playwright/snapshots/right-panel/notification-panel.spec.ts/empty-linux.png index 7d8884dc4d..5547e7621b 100644 Binary files a/playwright/snapshots/right-panel/notification-panel.spec.ts/empty-linux.png and b/playwright/snapshots/right-panel/notification-panel.spec.ts/empty-linux.png differ diff --git a/playwright/snapshots/threads/threads.spec.ts/Reply-to-the-location-on-ThreadView-linux.png b/playwright/snapshots/threads/threads.spec.ts/Reply-to-the-location-on-ThreadView-linux.png index 281f1cebe5..d30d8e98a8 100644 Binary files a/playwright/snapshots/threads/threads.spec.ts/Reply-to-the-location-on-ThreadView-linux.png and b/playwright/snapshots/threads/threads.spec.ts/Reply-to-the-location-on-ThreadView-linux.png differ diff --git a/playwright/snapshots/user-view/user-view.spec.ts/user-info-linux.png b/playwright/snapshots/user-view/user-view.spec.ts/user-info-linux.png index 61ab660157..0d4e64813c 100644 Binary files a/playwright/snapshots/user-view/user-view.spec.ts/user-info-linux.png and b/playwright/snapshots/user-view/user-view.spec.ts/user-info-linux.png differ diff --git a/res/css/views/right_panel/_BaseCard.pcss b/res/css/views/right_panel/_BaseCard.pcss index 67eb9b7e49..692f7d23b3 100644 --- a/res/css/views/right_panel/_BaseCard.pcss +++ b/res/css/views/right_panel/_BaseCard.pcss @@ -27,7 +27,7 @@ limitations under the License. .mx_BaseCard_header { height: 64px; - padding: var(--cpd-space-3x); + padding: var(--cpd-space-4x); box-sizing: border-box; /* changing the color from $separator to transparent as it is the best visual output during the transition period. This will be @@ -36,8 +36,13 @@ limitations under the License. display: flex; align-items: center; justify-content: space-between; - gap: var(--cpd-space-2x); + gap: var(--cpd-space-3x); flex-shrink: 0; + border-block-end: var(--cpd-border-width-1) solid $separator; + + .mx_BaseCard_header_spacer { + flex: 1; + } > h2 { margin: 0 44px; @@ -155,52 +160,6 @@ limitations under the License. } } -.mx_BaseCard_back, -.mx_BaseCard_close { - flex-shrink: 0; - position: relative; - /* @TODO(kerrya) background colours here are not semantic - these buttons to be replaced with IconButton after secondary variant is added - https://github.com/vector-im/compound/issues/279 */ - background-color: var(--cpd-color-bg-subtle-secondary); - width: var(--BaseCard_header-button-size); - height: var(--BaseCard_header-button-size); - border-radius: 50%; - - &:hover { - background-color: var(--cpd-color-bg-subtle-primary); - } - - &::before { - content: ""; - position: absolute; - height: inherit; - width: inherit; - top: 0; - left: 0; - mask-repeat: no-repeat; - mask-position: center; - mask-size: 20px; - background-color: var(--cpd-color-icon-secondary); - } -} - -.mx_BaseCard_back { - order: 0; /* always first! */ - &::before { - transform: rotate(90deg); - mask-size: 22px; - mask-image: url("$(res)/img/feather-customised/chevron-down.svg"); - } -} - -.mx_BaseCard_close { - order: 999; /* always last */ - &::before { - mask-image: url("@vector-im/compound-design-tokens/icons/close.svg"); - } -} - .mx_ContextualMenu_wrapper.mx_BaseCard_header_title { .mx_ContextualMenu { position: initial; @@ -235,7 +194,3 @@ limitations under the License. } } } - -.mx_BaseCard_headerProp { - flex: 1 1 100%; -} diff --git a/res/themes/dark/css/_dark.pcss b/res/themes/dark/css/_dark.pcss index 326debc062..d398fb5967 100644 --- a/res/themes/dark/css/_dark.pcss +++ b/res/themes/dark/css/_dark.pcss @@ -14,7 +14,7 @@ $overlay-background: var(--cpd-color-alpha-gray-1300); $panels: var(--cpd-color-bg-subtle-secondary); $panel-actions: var(--cpd-color-alpha-gray-300); -$separator: var(--cpd-color-alpha-gray-400); +$separator: var(--cpd-color-gray-400); /* ******************** */ diff --git a/res/themes/legacy-dark/css/_legacy-dark.pcss b/res/themes/legacy-dark/css/_legacy-dark.pcss index 7e14e85f10..c6840f5b90 100644 --- a/res/themes/legacy-dark/css/_legacy-dark.pcss +++ b/res/themes/legacy-dark/css/_legacy-dark.pcss @@ -105,7 +105,7 @@ $overlay-background: rgba($background, 0.85); $panels: rgba($system, 0.9); $panel-actions: $roomtile-selected-bg-color; -$separator: var(--cpd-color-alpha-gray-400); +$separator: var(--cpd-color-gray-400); /** * Creating a `semantic` color scale. This will not be needed with the new diff --git a/res/themes/legacy-light/css/_legacy-light.pcss b/res/themes/legacy-light/css/_legacy-light.pcss index 5f9b8fd452..e40fbde72b 100644 --- a/res/themes/legacy-light/css/_legacy-light.pcss +++ b/res/themes/legacy-light/css/_legacy-light.pcss @@ -163,7 +163,7 @@ $overlay-background: rgba($background, 0.85); $panels: rgba($system, 0.9); $panel-actions: $roomtile-selected-bg-color; -$separator: var(--cpd-color-alpha-gray-400); +$separator: var(--cpd-color-gray-400); /* Legacy theme backports */ diff --git a/res/themes/light/css/_light.pcss b/res/themes/light/css/_light.pcss index 730c115514..1a237427f2 100644 --- a/res/themes/light/css/_light.pcss +++ b/res/themes/light/css/_light.pcss @@ -32,7 +32,7 @@ $overlay-background: var(--cpd-color-alpha-gray-1300); $panels: var(--cpd-color-bg-subtle-secondary); $panel-actions: var(--cpd-color-alpha-gray-300); -$separator: var(--cpd-color-alpha-gray-400); +$separator: var(--cpd-color-gray-400); $accent: var(--cpd-color-text-action-accent); $alert: var(--cpd-color-text-critical-primary); diff --git a/src/components/structures/ThreadPanel.tsx b/src/components/structures/ThreadPanel.tsx index 1fca77c27e..d9e1cf2aa8 100644 --- a/src/components/structures/ThreadPanel.tsx +++ b/src/components/structures/ThreadPanel.tsx @@ -229,7 +229,7 @@ const ThreadPanel: React.FC = ({ roomId, onClose, permalinkCreator }) => const roomContext = useContext(RoomContext); const timelinePanel = useRef(null); const card = useRef(null); - const closeButonRef = useRef(null); + const closeButonRef = useRef(null); const [filterOption, setFilterOption] = useState(ThreadFilterType.All); const [room, setRoom] = useState(null); diff --git a/src/components/views/right_panel/BaseCard.tsx b/src/components/views/right_panel/BaseCard.tsx index bb07426a11..d8b0c1b71b 100644 --- a/src/components/views/right_panel/BaseCard.tsx +++ b/src/components/views/right_panel/BaseCard.tsx @@ -14,12 +14,14 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { forwardRef, ReactNode, KeyboardEvent, Ref } from "react"; +import React, { forwardRef, ReactNode, KeyboardEvent, Ref, MouseEvent } from "react"; import classNames from "classnames"; +import { IconButton, Text } from "@vector-im/compound-web"; +import { Icon as CloseIcon } from "@vector-im/compound-design-tokens/icons/close.svg"; +import { Icon as ChevronLeftIcon } from "@vector-im/compound-design-tokens/icons/chevron-left.svg"; import AutoHideScrollbar from "../../structures/AutoHideScrollbar"; import { _t } from "../../../languageHandler"; -import AccessibleButton, { ButtonEvent } from "../elements/AccessibleButton"; import RightPanelStore from "../../../stores/right-panel/RightPanelStore"; import { backLabelForPhase } from "../../../stores/right-panel/RightPanelStorePhases"; import { CardContext } from "./context"; @@ -34,13 +36,13 @@ interface IProps { ariaLabelledBy?: string; withoutScrollContainer?: boolean; closeLabel?: string; - onClose?(ev: ButtonEvent): void; - onBack?(ev: ButtonEvent): void; + onClose?(ev: MouseEvent): void; + onBack?(ev: MouseEvent): void; onKeyDown?(ev: KeyboardEvent): void; cardState?: any; ref?: Ref; // Ref for the 'close' button the the card - closeButtonRef?: Ref; + closeButtonRef?: Ref; children: ReactNode; } @@ -81,26 +83,39 @@ const BaseCard: React.FC = forwardRef( ) => { let backButton; const cardHistory = RightPanelStore.instance.roomPhaseHistory; - if (cardHistory.length > 1) { + if (cardHistory.length > 1 && !hideHeaderButtons) { const prevCard = cardHistory[cardHistory.length - 2]; - const onBackClick = (ev: ButtonEvent): void => { + const onBackClick = (ev: MouseEvent): void => { onBack?.(ev); RightPanelStore.instance.popCard(); }; const label = backLabelForPhase(prevCard.phase) ?? _t("action|back"); - backButton = ; + backButton = ( + + + + ); } let closeButton; - if (onClose) { + if (onClose && !hideHeaderButtons) { closeButton = ( - + tooltip={closeLabel ?? _t("action|close")} + subtleBackground + > + + ); } @@ -108,16 +123,6 @@ const BaseCard: React.FC = forwardRef( children = {children}; } - let headerButtons: React.ReactElement | undefined; - if (!hideHeaderButtons) { - headerButtons = ( - <> - {backButton} - {closeButton} - - ); - } - const shouldRenderHeader = header || !hideHeaderButtons; return ( @@ -132,8 +137,15 @@ const BaseCard: React.FC = forwardRef( > {shouldRenderHeader && (
- {headerButtons} -
{header}
+ {backButton} + {typeof header === "string" ? ( + + {header} + + ) : ( + header ??
+ )} + {closeButton}
)} {children} diff --git a/src/components/views/right_panel/UserInfo.tsx b/src/components/views/right_panel/UserInfo.tsx index 5f9830f5d6..165f0707e0 100644 --- a/src/components/views/right_panel/UserInfo.tsx +++ b/src/components/views/right_panel/UserInfo.tsx @@ -1778,7 +1778,7 @@ const UserInfo: React.FC = ({ user, room, onClose, phase = RightPanelPha return (
+

+ Example 1 +

+ +
with crypto enabled renders 1`] = `
-
+ Profile +

+
with crypto enabled should render a deactivate button for
-
+ Profile +

+
should render invite 1`] = ` class="mx_BaseCard_header" >
should render invite when room in not availabl class="mx_BaseCard_header" >