diff --git a/.github/actions/download-verify-element-tarball/action.yml b/.github/actions/download-verify-element-tarball/action.yml index a64bc3241b..40855b85c6 100644 --- a/.github/actions/download-verify-element-tarball/action.yml +++ b/.github/actions/download-verify-element-tarball/action.yml @@ -31,7 +31,9 @@ runs: - name: Move webapp to out-file-path shell: bash - run: mv ${{ runner.temp }}/download-verify-element-tarball/webapp ${{ inputs.out-file-path }} + run: mv ${{ runner.temp }}/download-verify-element-tarball/webapp "$OUT_PATH" + env: + OUT_PATH: ${{ inputs.out-file-path }} - name: Clean up temp directory shell: bash diff --git a/.github/actions/setup-playwright/action.yml b/.github/actions/setup-playwright/action.yml new file mode 100644 index 0000000000..ffddff1dc6 --- /dev/null +++ b/.github/actions/setup-playwright/action.yml @@ -0,0 +1,49 @@ +name: Setup playwright +description: Installs playwright browsers and sets up a cache +inputs: + needs-webkit: + description: Whether to install the additional dependencies for webkit + required: false + default: "false" + write-cache: + description: Whether to write the cache back + required: true +runs: + using: composite + steps: + - name: Calculate cache key + id: key + run: | + PW_VERSION=$(pnpm --silent -- playwright --version | awk '{print $2}') + echo "key=${PREFIX}-playwright-${PW_VERSION}" >> $GITHUB_OUTPUT + shell: bash + env: + PREFIX: ${{ runner.os }}-${{ runner.arch }} + + - name: Cache playwright binaries + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5 + if: inputs.write-cache == 'true' + id: cache + with: + path: ~/.cache/ms-playwright + key: ${{ steps.key.outputs.key }} + + # When running in merge queue only restore the cache, never write it + - name: Restore playwright binaries cache + uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5 + if: inputs.write-cache != 'true' + id: cache-restore + with: + path: ~/.cache/ms-playwright + key: ${{ steps.key.outputs.key }} + + - name: Install Playwright browsers + if: (steps.cache.outputs.cache-hit || steps.cache-restore.outputs.cache-hit) != 'true' + shell: bash + run: pnpm playwright install --with-deps + + # Some WebKit dependencies seem to lay outside the cache and will need to be installed separately + - name: Install system dependencies for WebKit + if: inputs.needs-webkit == 'true' && (steps.cache.outputs.cache-hit || steps.cache-restore.outputs.cache-hit) == 'true' + shell: bash + run: pnpm playwright install-deps webkit diff --git a/.github/renovate.json b/.github/renovate.json index 5afa859d07..ded20f15d7 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -2,6 +2,14 @@ "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": ["github>matrix-org/renovate-config-element-web"], "postUpdateOptions": ["pnpmDedupe"], + "packageRules": [ + { + "groupName": "testcontainers docker digests", + "groupSlug": "{{manager}}-docker-digests", + "matchManagers": ["custom.regex"], + "matchPackageNames": ["*"] + } + ], "customManagers": [ { "customType": "regex", diff --git a/.github/workflows/build-and-test.yaml b/.github/workflows/build-and-test.yaml index e13c537d0e..1c66b02414 100644 --- a/.github/workflows/build-and-test.yaml +++ b/.github/workflows/build-and-test.yaml @@ -18,8 +18,6 @@ on: push: # We do not build on push to develop as the merge_group check handles that branches: [staging, master] - repository_dispatch: - types: [element-web-notify] # support triggering from other workflows workflow_call: @@ -155,27 +153,11 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile - - name: Get installed Playwright version - id: playwright - run: echo "version=$(pnpm --silent -- playwright --version | awk '{print $2}')" >> $GITHUB_OUTPUT - - - name: Cache playwright binaries - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5 - id: playwright-cache + - name: Setup playwright + uses: ./.github/actions/setup-playwright with: - path: ~/.cache/ms-playwright - key: ${{ runner.os }}-${{ runner.arch }}-playwright-${{ steps.playwright.outputs.version }} - - - name: Install Playwright browsers - if: steps.playwright-cache.outputs.cache-hit != 'true' - working-directory: apps/web - run: pnpm playwright install --with-deps --no-shell - - - name: Install system dependencies for WebKit - # Some WebKit dependencies seem to lay outside the cache and will need to be installed separately - if: matrix.project == 'WebKit' && steps.playwright-cache.outputs.cache-hit == 'true' - working-directory: apps/web - run: pnpm playwright install-deps webkit + needs-webkit: ${{ matrix.project == 'WebKit' }} + write-cache: ${{ github.event_name != 'merge_group' }} # We skip tests tagged with @mergequeue when running on PRs, but run them in MQ and everywhere else - name: Run Playwright tests @@ -204,6 +186,7 @@ jobs: uses: element-hq/element-modules/.github/workflows/reusable-playwright-tests.yml@main # zizmor: ignore[unpinned-uses] with: webapp-artifact: webapp + reporter: blob prepare_ed: name: "Prepare Element Desktop" @@ -214,7 +197,7 @@ jobs: contents: read with: config: ${{ (github.event.pull_request.base.ref || github.ref_name) == 'develop' && 'element.io/nightly' || 'element.io/release' }} - version: ${{ (github.event.pull_request.base.ref || github.ref_name) == 'develop' && 'develop' || '' }} + version: ${{ case((github.event.pull_request.base.ref || github.ref_name) == 'develop' || github.event_name == 'merge_group', 'develop', '') }} webapp-artifact: webapp build_ed_windows: @@ -262,6 +245,7 @@ jobs: needs: - playwright_ew - downstream-modules + - prepare_ed - build_ed_windows - build_ed_linux - build_ed_macos diff --git a/.github/workflows/build_desktop_test.yaml b/.github/workflows/build_desktop_test.yaml index ba347fa5b3..4ee61b68c8 100644 --- a/.github/workflows/build_desktop_test.yaml +++ b/.github/workflows/build_desktop_test.yaml @@ -38,7 +38,7 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: - repository: ${{ github.repository == 'element-hq/element-web-pro' && 'element-hq/element-web' || github.repository }} + repository: element-hq/element-web persist-credentials: false - uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5 diff --git a/.github/workflows/build_develop.yml b/.github/workflows/build_develop.yml index 6f85402bb6..12821aab1a 100644 --- a/.github/workflows/build_develop.yml +++ b/.github/workflows/build_develop.yml @@ -111,7 +111,7 @@ jobs: running-workflow-name: "Build & Deploy develop.element.io" repo-token: ${{ secrets.GITHUB_TOKEN }} wait-interval: 10 - check-regexp: ^((?!SonarCloud|SonarQube|issue|board|label|Release|prepare|GitHub Pages|Upload).)*$ + check-regexp: ^((?!SonarCloud|SonarQube|issue|board|label|Release|prepare|GitHub Pages|Upload|Netlify).)*$ # We keep the latest develop.tar.gz on R2 instead of relying on the github artifact uploaded earlier # as the expires after 24h and requires auth to download. diff --git a/.github/workflows/merge-queue.yaml b/.github/workflows/merge-queue.yaml new file mode 100644 index 0000000000..1e4d7d3ede --- /dev/null +++ b/.github/workflows/merge-queue.yaml @@ -0,0 +1,29 @@ +# Tweaks the behaviour of Merge Queue to skip certain checks +name: Merge Queue tweaks +on: + merge_group: + types: [checks_requested] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }} + cancel-in-progress: true + +permissions: {} + +jobs: + run: + runs-on: ubuntu-24.04 + permissions: + statuses: write + steps: + # This is only needed as license/cla at time of writing seems to be extraordinarily flaky + # and Github doesn't support conditional checks between PR & merge queue. + # This is fine to do as a PR won't make it to merge queue until it has license/cla passing. + - name: Skip license/cla on merge queues + uses: guibranco/github-status-action-v2@9bfa8773cdbdc6c185747fd43cd7faa9d7c32f09 + with: + authToken: ${{ secrets.GITHUB_TOKEN }} + state: success + context: license/cla + sha: ${{ github.sha }} + target_url: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5a0f5166e3..b1a81af773 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -30,7 +30,8 @@ jobs: asset-path: dist/*.tar.gz expected-asset-count: 3 # Desktop has no dist script so we only target web here - dir: apps/web + dist-dir: apps/web + version-dirs: apps/web apps/desktop check: name: Post release checks diff --git a/.github/workflows/shared-component-visual-tests-netlify.yaml b/.github/workflows/shared-component-visual-tests-netlify.yaml index 1f9ae76826..e4b830406d 100644 --- a/.github/workflows/shared-component-visual-tests-netlify.yaml +++ b/.github/workflows/shared-component-visual-tests-netlify.yaml @@ -2,7 +2,9 @@ # It uploads the received images and diffs to netlify, printing the URLs to the console name: Upload Shared Component Visual Test Diffs on: - workflow_run: + # Privilege escalation necessary to deploy to Netlify + # 🚨 We must not execute any checked out code here. + workflow_run: # zizmor: ignore[dangerous-triggers] workflows: ["Shared Component Visual Tests"] types: - completed diff --git a/.github/workflows/shared-component-visual-tests.yaml b/.github/workflows/shared-component-visual-tests.yaml index 06b2d11b1c..6adc4024c9 100644 --- a/.github/workflows/shared-component-visual-tests.yaml +++ b/.github/workflows/shared-component-visual-tests.yaml @@ -36,22 +36,10 @@ jobs: working-directory: packages/shared-components run: pnpm install --frozen-lockfile - - name: Get installed Playwright version - working-directory: packages/shared-components - id: playwright - run: echo "version=$(pnpm list @playwright/test --depth=0 --json | jq -r '.[].devDependencies["@playwright/test"].version')" >> $GITHUB_OUTPUT - - - name: Cache playwright binaries - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5 - id: playwright-cache + - name: Setup playwright + uses: ./.github/actions/setup-playwright with: - path: ~/.cache/ms-playwright - key: ${{ runner.os }}-${{ runner.arch }}-playwright-${{ steps.playwright.outputs.version }}-onlyshell - - - name: Install Playwright browsers - working-directory: packages/shared-components - if: steps.playwright-cache.outputs.cache-hit != 'true' - run: "pnpm playwright install --with-deps --only-shell" + write-cache: ${{ github.event_name != 'merge_group' }} - name: Run Visual tests working-directory: packages/shared-components diff --git a/.github/workflows/sonarqube.yml b/.github/workflows/sonarqube.yml index 73efd48ba3..e934f05ad1 100644 --- a/.github/workflows/sonarqube.yml +++ b/.github/workflows/sonarqube.yml @@ -1,6 +1,8 @@ name: SonarQube on: - workflow_run: + # Privilege escalation necessary to call upon SonarCloud + # 🚨 We must not execute any checked out code here. + workflow_run: # zizmor: ignore[dangerous-triggers] workflows: ["Tests"] types: - completed diff --git a/.github/workflows/static_analysis.yaml b/.github/workflows/static_analysis.yaml index f3052ff373..3dd7da0e39 100644 --- a/.github/workflows/static_analysis.yaml +++ b/.github/workflows/static_analysis.yaml @@ -5,8 +5,6 @@ on: branches: [develop, master] merge_group: types: [checks_requested] - repository_dispatch: - types: [element-web-notify] concurrency: group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }} cancel-in-progress: true diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b9ffa23c99..60730451f6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -5,8 +5,6 @@ on: types: [checks_requested] push: branches: [develop, master] - repository_dispatch: - types: [element-web-notify] workflow_call: inputs: disable_coverage: @@ -151,22 +149,10 @@ jobs: packages/shared-components/node_modules/.vite/vitest key: ${{ hashFiles('pnpm-lock.yaml') }} - - name: Get installed Playwright version - working-directory: packages/shared-components - id: playwright - run: echo "version=$(pnpm list @playwright/test --depth=0 --json | jq -r '.[].devDependencies["@playwright/test"].version')" >> $GITHUB_OUTPUT - - - name: Cache playwright binaries - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5 - id: playwright-cache + - name: Setup playwright + uses: ./.github/actions/setup-playwright with: - path: ~/.cache/ms-playwright - key: ${{ runner.os }}-${{ runner.arch }}-playwright-${{ steps.playwright.outputs.version }}-onlyshell - - - name: Install Playwright browsers - working-directory: packages/shared-components - if: steps.playwright-cache.outputs.cache-hit != 'true' - run: "pnpm playwright install --with-deps --only-shell" + write-cache: ${{ github.event_name != 'merge_group' }} - name: Run tests working-directory: "packages/shared-components" diff --git a/CHANGELOG.md b/CHANGELOG.md index 78659f2037..4b87749adb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,32 @@ +Changes in [1.12.15](https://github.com/element-hq/element-web/releases/tag/v1.12.15) (2026-04-08) +================================================================================================== +Fixes Desktop release workflow. + +This release is identical to v1.12.14 otherwise. + +Changes in [1.12.14](https://github.com/element-hq/element-web/releases/tag/v1.12.14) (2026-04-07) +================================================================================================== +## ✨ Features + +* Add analytics tracking for URL previews ([#32659](https://github.com/element-hq/element-web/pull/32659)). Contributed by @Half-Shot. +* Collapsible Room List - Clicking on separator should expand to last set width ([#32909](https://github.com/element-hq/element-web/pull/32909)). Contributed by @MidhunSureshR. +* RoomList: improve performance ([#32919](https://github.com/element-hq/element-web/pull/32919)). Contributed by @florianduros. +* Implement collapsible panels for the new room list ([#32742](https://github.com/element-hq/element-web/pull/32742)). Contributed by @MidhunSureshR. +* Hide the names of banned users behind a spoiler tag (attempt 2) ([#32636](https://github.com/element-hq/element-web/pull/32636)). Contributed by @andybalaam. + +## 🐛 Bug Fixes + +* Use the code signing Subject Name as basis for Tray GUID on Windows ([#32939](https://github.com/element-hq/element-web/pull/32939)). Contributed by @t3chguy. +* Ensure the incoming verification request appears above the please verify prompt ([#32931](https://github.com/element-hq/element-web/pull/32931)). Contributed by @andybalaam. +* Collapsible Room List - Prevent any interaction with the separator when the panel is expanded ([#32910](https://github.com/element-hq/element-web/pull/32910)). Contributed by @MidhunSureshR. +* Fix icon size of badges in right panel ([#32952](https://github.com/element-hq/element-web/pull/32952)). Contributed by @florianduros. +* Fix room list often showing the wrong icons for calls ([#32881](https://github.com/element-hq/element-web/pull/32881)). Contributed by @robintown. +* Fix emoticon slash commands including stale buffers ([#32928](https://github.com/element-hq/element-web/pull/32928)). Contributed by @t3chguy. +* Fix presence indicators not showing without cache ([#32880](https://github.com/element-hq/element-web/pull/32880)). Contributed by @DLCSharp. +* Show space name instead of 'Empty room' after creation ([#32886](https://github.com/element-hq/element-web/pull/32886)). Contributed by @gugaribeiro05. +* Strip ephemeral query params from OIDC redirect URI ([#32875](https://github.com/element-hq/element-web/pull/32875)). Contributed by @azmeuk. + + Changes in [1.12.13](https://github.com/element-hq/element-web/releases/tag/v1.12.13) (2026-03-24) ================================================================================================== ## 🦖 Deprecations diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 4849ad9d69..a4f9c68437 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -3,7 +3,7 @@ "productName": "Element", "main": "lib/electron-main.js", "exports": "./lib/electron-main.js", - "version": "1.12.13", + "version": "1.12.15", "description": "Element: the future of secure communication", "author": { "name": "Element", diff --git a/apps/desktop/playwright/Dockerfile b/apps/desktop/playwright/Dockerfile index e2fa600efc..dabf89dd46 100644 --- a/apps/desktop/playwright/Dockerfile +++ b/apps/desktop/playwright/Dockerfile @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/playwright:v1.58.2-jammy@sha256:4698a73749c5848d3f5fcd42a2174d172fcad2b2283e087843b115424303a565 +FROM mcr.microsoft.com/playwright:v1.59.1-jammy@sha256:8a0360d39d1973be506dd59002904a774f6d697d4946c94063b3fd006461c8ff WORKDIR /work/element-desktop diff --git a/apps/web/package.json b/apps/web/package.json index ca6153e2cc..98b6f8c876 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,6 +1,6 @@ { "name": "element-web", - "version": "1.12.13", + "version": "1.12.15", "description": "Element: the future of secure communication", "author": "New Vector Ltd.", "repository": { @@ -67,7 +67,7 @@ "emojibase-regex": "^17.0.0", "escape-html": "^1.0.3", "file-saver": "^2.0.5", - "filesize": "11.0.13", + "filesize": "11.0.15", "github-markdown-css": "^5.5.1", "glob-to-regexp": "^0.4.1", "highlight.js": "^11.3.1", @@ -78,7 +78,7 @@ "jsrsasign": "^11.0.0", "jszip": "^3.7.0", "katex": "^0.16.0", - "lodash": "npm:lodash-es@^4.17.21", + "lodash": "npm:lodash-es@4.18.1", "maplibre-gl": "^5.0.0", "matrix-encrypt-attachment": "^1.0.3", "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop", @@ -89,7 +89,7 @@ "opus-recorder": "^8.0.3", "pako": "^2.0.3", "png-chunks-extract": "^1.0.0", - "posthog-js": "1.360.2", + "posthog-js": "1.364.7", "qrcode": "1.5.4", "re-resizable": "6.11.2", "react": "catalog:", diff --git a/apps/web/playwright/e2e/devtools/lowbandwidth.spec.ts b/apps/web/playwright/e2e/devtools/lowbandwidth.spec.ts new file mode 100644 index 0000000000..d24ac69b94 --- /dev/null +++ b/apps/web/playwright/e2e/devtools/lowbandwidth.spec.ts @@ -0,0 +1,61 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { test, expect } from "../../element-web-test"; +import { getSampleFilePath } from "../../sample-files"; + +test.describe("Devtools", () => { + test.use({ + displayName: "Alice", + }); + + test("should allow enabling low bandwidth mode", async ({ page, homeserver, user, app }) => { + // Upload a picture + const userSettings = await app.settings.openUserSettings("Account"); + const profileSettings = userSettings.locator(".mx_UserProfileSettings"); + await profileSettings.getByAltText("Upload").setInputFiles(getSampleFilePath("riot.png")); + await app.closeDialog(); + + // Create an initial room. + const createRoomDialog = await app.openCreateRoomDialog(); + await createRoomDialog.getByRole("textbox", { name: "Name" }).fill("Test Room"); + await createRoomDialog.getByRole("button", { name: "Create room" }).click(); + + const composer = app.getComposer().locator("[contenteditable]"); + await composer.fill("/devtools"); + await composer.press("Enter"); + const dialog = page.locator(".mx_Dialog"); + await dialog.getByLabel("Developer mode").check(); + await dialog.getByLabel("Disable bandwidth-heavy features").click(); + // Wait for refresh. + await page.waitForEvent("domcontentloaded"); + await app.viewRoomByName("Test Room"); + + // This only appears when encryption has been disabled in the client. + await expect(page.getByText("The encryption used by this room isn't supported.")).toBeVisible(); + + // None of these should be requested. + let hasSentTyping = false; + let hasRequestedThumbnail = false; + await page.route("**/_matrix/client/v3/rooms/*/typing/*", async (route) => { + hasSentTyping = true; + await route.fulfill({ json: {} }); + }); + await page.route("**/_matrix/media/v3/thumbnail/**", async (route) => { + hasRequestedThumbnail = true; + await route.fulfill({ json: {} }); + }); + await page.route("**/_matrix/client/v1/media/thumbnail/**", async (route) => { + hasRequestedThumbnail = true; + await route.fulfill({ json: {} }); + }); + + await composer.pressSequentially("Provoke typing request", { delay: 5 }); + expect(hasSentTyping).toEqual(false); + expect(hasRequestedThumbnail).toEqual(false); + }); +}); diff --git a/apps/web/playwright/e2e/settings/account-user-settings-tab.spec.ts b/apps/web/playwright/e2e/settings/account-user-settings-tab.spec.ts index 07c3a9592d..b3ec952b01 100644 --- a/apps/web/playwright/e2e/settings/account-user-settings-tab.spec.ts +++ b/apps/web/playwright/e2e/settings/account-user-settings-tab.spec.ts @@ -24,7 +24,7 @@ test.describe("Account user settings tab", () => { }, }); - test("should be rendered properly", { tag: "@screenshot" }, async ({ uut, user }) => { + test("should be rendered properly", { tag: "@screenshot" }, async ({ uut, user, axe }) => { await expect(uut).toMatchScreenshot("account.png"); // Assert that the top heading is rendered @@ -70,6 +70,8 @@ test.describe("Account user settings tab", () => { await expect(accountManagementSection.getByRole("button", { name: "Deactivate Account" })).toHaveClass( /mx_AccessibleButton_kind_danger/, ); + + await expect(axe).toHaveNoViolations(); }); test("should respond to small screen sizes", { tag: "@screenshot" }, async ({ page, uut }) => { diff --git a/apps/web/playwright/e2e/settings/appearance-user-settings-tab/appearance-user-settings-tab.spec.ts b/apps/web/playwright/e2e/settings/appearance-user-settings-tab/appearance-user-settings-tab.spec.ts index c6af256a5d..457c5c17cf 100644 --- a/apps/web/playwright/e2e/settings/appearance-user-settings-tab/appearance-user-settings-tab.spec.ts +++ b/apps/web/playwright/e2e/settings/appearance-user-settings-tab/appearance-user-settings-tab.spec.ts @@ -13,7 +13,7 @@ test.describe("Appearance user settings tab", () => { displayName: "Hanako", }); - test("should be rendered properly", { tag: "@screenshot" }, async ({ page, user, app }) => { + test("should be rendered properly", { tag: "@screenshot" }, async ({ page, user, app, axe }) => { const tab = await app.settings.openUserSettings("Appearance"); // Click "Show advanced" link button @@ -23,6 +23,8 @@ test.describe("Appearance user settings tab", () => { await expect(tab.getByRole("button", { name: "Hide advanced" })).toBeVisible(); await expect(tab).toMatchScreenshot("appearance-tab.png"); + + await expect(axe).toHaveNoViolations(); }); test( diff --git a/apps/web/playwright/e2e/settings/appearance-user-settings-tab/theme-choice-panel.spec.ts b/apps/web/playwright/e2e/settings/appearance-user-settings-tab/theme-choice-panel.spec.ts index 8b7cf720c8..cb38f923e9 100644 --- a/apps/web/playwright/e2e/settings/appearance-user-settings-tab/theme-choice-panel.spec.ts +++ b/apps/web/playwright/e2e/settings/appearance-user-settings-tab/theme-choice-panel.spec.ts @@ -23,7 +23,7 @@ test.describe("Appearance user settings tab", () => { test( "should be rendered with the light theme selected", { tag: "@screenshot" }, - async ({ page, app, util }) => { + async ({ page, app, util, axe }) => { // Assert that 'Match system theme' is not checked await expect(util.getMatchSystemThemeSwitch()).not.toBeChecked(); @@ -34,6 +34,8 @@ test.describe("Appearance user settings tab", () => { await expect(util.getHighContrastTheme()).not.toBeChecked(); await expect(util.getThemePanel()).toMatchScreenshot("theme-panel-light.png"); + + await expect(axe).toHaveNoViolations(); }, ); diff --git a/apps/web/playwright/e2e/settings/device-management.spec.ts b/apps/web/playwright/e2e/settings/device-management.spec.ts index 9f60b2433c..265ec9c877 100644 --- a/apps/web/playwright/e2e/settings/device-management.spec.ts +++ b/apps/web/playwright/e2e/settings/device-management.spec.ts @@ -23,7 +23,7 @@ test.describe("Device manager", () => { } }); - test("should display sessions", async ({ page, app }) => { + test("should display sessions", async ({ page, app, axe }) => { await app.settings.openUserSettings("Sessions"); const tab = page.locator(".mx_SettingsTab"); @@ -85,7 +85,7 @@ test.describe("Device manager", () => { // session name updated in details await expect(firstSession.locator(".mx_DeviceDetailHeading h4").getByText(sessionName)).toBeVisible(); // and main list item - await expect(firstSession.locator(".mx_DeviceTile h4").getByText(sessionName)).toBeVisible(); + await expect(firstSession.locator(".mx_DeviceTile h3").getByText(sessionName)).toBeVisible(); // sign out using the device details sign out await firstSession.getByRole("button", { name: "Remove this session" }).click(); @@ -96,5 +96,7 @@ test.describe("Device manager", () => { // no other sessions or security recommendations sections when only one session await expect(tab.getByText("Other sessions")).not.toBeVisible(); await expect(tab.getByTestId("security-recommendations-section")).not.toBeVisible(); + + await expect(axe).toHaveNoViolations(); }); }); diff --git a/apps/web/playwright/e2e/settings/encryption-user-tab/advanced.spec.ts b/apps/web/playwright/e2e/settings/encryption-user-tab/advanced.spec.ts index 1d8ae102fd..0167a321b7 100644 --- a/apps/web/playwright/e2e/settings/encryption-user-tab/advanced.spec.ts +++ b/apps/web/playwright/e2e/settings/encryption-user-tab/advanced.spec.ts @@ -16,7 +16,7 @@ test.describe("Advanced section in Encryption tab", () => { await bootstrapCrossSigningForClient(clientHandle, credentials, true); }); - test("should show the encryption details", { tag: "@screenshot" }, async ({ page, app, util }) => { + test("should show the encryption details", { tag: "@screenshot" }, async ({ page, app, util, axe }) => { await util.openEncryptionTab(); const section = util.getEncryptionDetailsSection(); @@ -26,6 +26,8 @@ test.describe("Advanced section in Encryption tab", () => { await expect(section).toMatchScreenshot("encryption-details.png", { mask: [section.getByTestId("deviceId"), section.getByTestId("sessionKey")], }); + + await expect(axe).toHaveNoViolations(); }); test("should show the import room keys dialog", async ({ page, app, util }) => { diff --git a/apps/web/playwright/e2e/settings/general-room-settings-tab.spec.ts b/apps/web/playwright/e2e/settings/general-room-settings-tab.spec.ts index 376412914a..e3f0963b59 100644 --- a/apps/web/playwright/e2e/settings/general-room-settings-tab.spec.ts +++ b/apps/web/playwright/e2e/settings/general-room-settings-tab.spec.ts @@ -20,7 +20,7 @@ test.describe("General room settings tab", () => { await app.viewRoomByName(roomName); }); - test("should be rendered properly", { tag: "@screenshot" }, async ({ page, app }) => { + test("should be rendered properly", { tag: "@screenshot" }, async ({ page, app, axe }) => { const settings = await app.settings.openRoomSettings("General"); // Assert that "Show less" details element is rendered @@ -34,6 +34,9 @@ test.describe("General room settings tab", () => { // Assert that "Show more" details element is rendered instead of "Show more" await expect(settings.getByText("Show less")).not.toBeVisible(); await expect(settings.getByText("Show more")).toBeVisible(); + + axe.disableRules("color-contrast"); // XXX: We have some known contrast issues here + await expect(axe).toHaveNoViolations(); }); test("long address should not cause dialog to overflow", { tag: "@no-webkit" }, async ({ page, app, user }) => { diff --git a/apps/web/playwright/e2e/settings/preferences-user-settings-tab.spec.ts b/apps/web/playwright/e2e/settings/preferences-user-settings-tab.spec.ts index 8adbc74cc5..5414055534 100644 --- a/apps/web/playwright/e2e/settings/preferences-user-settings-tab.spec.ts +++ b/apps/web/playwright/e2e/settings/preferences-user-settings-tab.spec.ts @@ -25,7 +25,7 @@ test.describe("Preferences user settings tab", () => { labsFlags: ["feature_new_room_list"], }); - test("should be rendered properly", { tag: "@screenshot" }, async ({ app, page, user }) => { + test("should be rendered properly", { tag: "@screenshot" }, async ({ app, page, user, axe }) => { await page.setViewportSize({ width: 1024, height: 4000 }); const tab = await app.settings.openUserSettings("Preferences"); // Assert that the top heading is rendered @@ -39,6 +39,8 @@ test.describe("Preferences user settings tab", () => { } `, }); + + await expect(axe).toHaveNoViolations(); }); test("should be able to change the app language", { tag: ["@no-firefox", "@no-webkit"] }, async ({ uut, user }) => { diff --git a/apps/web/playwright/e2e/settings/quick-settings-menu.spec.ts b/apps/web/playwright/e2e/settings/quick-settings-menu.spec.ts index e58d523c21..37d9adc458 100644 --- a/apps/web/playwright/e2e/settings/quick-settings-menu.spec.ts +++ b/apps/web/playwright/e2e/settings/quick-settings-menu.spec.ts @@ -8,11 +8,13 @@ Please see LICENSE files in the repository root for full details. import { test, expect } from "../../element-web-test"; test.describe("Quick settings menu", () => { - test("should be rendered properly", { tag: "@screenshot" }, async ({ app, page, user }) => { + test("should be rendered properly", { tag: "@screenshot" }, async ({ app, page, user, axe }) => { await page.getByRole("button", { name: "Quick settings" }).click(); // Assert that the top heading is renderedc const settings = page.getByTestId("quick-settings-menu"); await expect(settings).toBeVisible(); await expect(settings).toMatchScreenshot("quick-settings.png"); + + await expect(axe).toHaveNoViolations(); }); }); diff --git a/apps/web/playwright/e2e/settings/room-settings/roles-permissions-room-settings-tab.spec.ts b/apps/web/playwright/e2e/settings/room-settings/roles-permissions-room-settings-tab.spec.ts index e575f71b72..c646e613c4 100644 --- a/apps/web/playwright/e2e/settings/room-settings/roles-permissions-room-settings-tab.spec.ts +++ b/apps/web/playwright/e2e/settings/room-settings/roles-permissions-room-settings-tab.spec.ts @@ -25,7 +25,7 @@ test.describe("Roles & Permissions room settings tab", () => { settings = await app.settings.openRoomSettings("Roles & Permissions"); }); - test("should be able to change the role of a user", async ({ page, app, user }) => { + test("should be able to change the role of a user", async ({ page, app, user, axe }) => { const privilegedUserSection = settings.locator(".mx_SettingsFieldset").first(); const applyButton = privilegedUserSection.getByRole("button", { name: "Apply" }); @@ -55,5 +55,7 @@ test.describe("Roles & Permissions room settings tab", () => { settings = await app.settings.openRoomSettings("Roles & Permissions"); combobox = privilegedUserSection.getByRole("combobox", { name: user.userId }); await expect(combobox).toHaveValue("50"); + + await expect(axe).toHaveNoViolations(); }); }); diff --git a/apps/web/playwright/e2e/settings/security-user-settings-tab.spec.ts b/apps/web/playwright/e2e/settings/security-user-settings-tab.spec.ts index 377cae7495..25f430c68d 100644 --- a/apps/web/playwright/e2e/settings/security-user-settings-tab.spec.ts +++ b/apps/web/playwright/e2e/settings/security-user-settings-tab.spec.ts @@ -32,12 +32,14 @@ test.describe("Security user settings tab", () => { }); test.describe("AnalyticsLearnMoreDialog", () => { - test("should be rendered properly", { tag: "@screenshot" }, async ({ app, page, user }) => { + test("should be rendered properly", { tag: "@screenshot" }, async ({ app, page, user, axe }) => { const tab = await app.settings.openUserSettings("Security"); await tab.getByRole("button", { name: "Learn more" }).click(); await expect(page.locator(".mx_AnalyticsLearnMoreDialog_wrapper .mx_Dialog")).toMatchScreenshot( "Security-user-settings-tab-with-posthog-enable-b5d89-csLearnMoreDialog-should-be-rendered-properly-1.png", ); + + await expect(axe).toHaveNoViolations(); }); }); diff --git a/apps/web/playwright/pages/network.ts b/apps/web/playwright/pages/network.ts index 3296fc8779..c007834113 100644 --- a/apps/web/playwright/pages/network.ts +++ b/apps/web/playwright/pages/network.ts @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com Please see LICENSE files in the repository root for full details. */ -import type { Page, Request, Route } from "@playwright/test"; +import type { Page, Request, Route, Disposable } from "@playwright/test"; import type { Client } from "./client"; /** @@ -16,7 +16,7 @@ import type { Client } from "./client"; */ export class Network { private isOffline = false; - private setupPromise?: Promise; + private setupPromise?: Promise; constructor( private page: Page, diff --git a/apps/web/playwright/snapshots/left-panel/room-list-panel/room-list-filter-sort.spec.ts/filter-menu-linux.png b/apps/web/playwright/snapshots/left-panel/room-list-panel/room-list-filter-sort.spec.ts/filter-menu-linux.png deleted file mode 100644 index fe056af3d3..0000000000 Binary files a/apps/web/playwright/snapshots/left-panel/room-list-panel/room-list-filter-sort.spec.ts/filter-menu-linux.png and /dev/null differ diff --git a/apps/web/playwright/snapshots/left-panel/room-list-panel/room-list-panel.spec.ts/room-list-panel-smallscreen-linux.png b/apps/web/playwright/snapshots/left-panel/room-list-panel/room-list-panel.spec.ts/room-list-panel-smallscreen-linux.png index dd26980a18..ef53f1d6fb 100644 Binary files a/apps/web/playwright/snapshots/left-panel/room-list-panel/room-list-panel.spec.ts/room-list-panel-smallscreen-linux.png and b/apps/web/playwright/snapshots/left-panel/room-list-panel/room-list-panel.spec.ts/room-list-panel-smallscreen-linux.png differ diff --git a/apps/web/playwright/snapshots/oidc/oidc-native.spec.ts/token-expired-linux.png b/apps/web/playwright/snapshots/oidc/oidc-native.spec.ts/token-expired-linux.png deleted file mode 100644 index e85b83f2f1..0000000000 Binary files a/apps/web/playwright/snapshots/oidc/oidc-native.spec.ts/token-expired-linux.png and /dev/null differ diff --git a/apps/web/playwright/snapshots/settings/account-user-settings-tab.spec.ts/account-linux.png b/apps/web/playwright/snapshots/settings/account-user-settings-tab.spec.ts/account-linux.png index bd01876a5a..8cd0cbf863 100644 Binary files a/apps/web/playwright/snapshots/settings/account-user-settings-tab.spec.ts/account-linux.png and b/apps/web/playwright/snapshots/settings/account-user-settings-tab.spec.ts/account-linux.png differ diff --git a/apps/web/playwright/snapshots/settings/account-user-settings-tab.spec.ts/account-smallscreen-linux.png b/apps/web/playwright/snapshots/settings/account-user-settings-tab.spec.ts/account-smallscreen-linux.png index dd449fae6c..14fd316f79 100644 Binary files a/apps/web/playwright/snapshots/settings/account-user-settings-tab.spec.ts/account-smallscreen-linux.png and b/apps/web/playwright/snapshots/settings/account-user-settings-tab.spec.ts/account-smallscreen-linux.png differ diff --git a/apps/web/playwright/snapshots/settings/encryption-user-tab/encryption-tab.spec.ts/default-tab-linux.png b/apps/web/playwright/snapshots/settings/encryption-user-tab/encryption-tab.spec.ts/default-tab-linux.png index bdee266c25..efdd7e3cba 100644 Binary files a/apps/web/playwright/snapshots/settings/encryption-user-tab/encryption-tab.spec.ts/default-tab-linux.png and b/apps/web/playwright/snapshots/settings/encryption-user-tab/encryption-tab.spec.ts/default-tab-linux.png differ diff --git a/apps/web/playwright/snapshots/settings/general-room-settings-tab.spec.ts/General-room-settings-tab-should-be-rendered-properly-1-linux.png b/apps/web/playwright/snapshots/settings/general-room-settings-tab.spec.ts/General-room-settings-tab-should-be-rendered-properly-1-linux.png index 2d0190c601..48625def3a 100644 Binary files a/apps/web/playwright/snapshots/settings/general-room-settings-tab.spec.ts/General-room-settings-tab-should-be-rendered-properly-1-linux.png and b/apps/web/playwright/snapshots/settings/general-room-settings-tab.spec.ts/General-room-settings-tab-should-be-rendered-properly-1-linux.png differ diff --git a/apps/web/playwright/snapshots/settings/preferences-user-settings-tab.spec.ts/Preferences-user-settings-tab-should-be-rendered-properly-1-linux.png b/apps/web/playwright/snapshots/settings/preferences-user-settings-tab.spec.ts/Preferences-user-settings-tab-should-be-rendered-properly-1-linux.png index 2a998720e9..ee058b786a 100644 Binary files a/apps/web/playwright/snapshots/settings/preferences-user-settings-tab.spec.ts/Preferences-user-settings-tab-should-be-rendered-properly-1-linux.png and b/apps/web/playwright/snapshots/settings/preferences-user-settings-tab.spec.ts/Preferences-user-settings-tab-should-be-rendered-properly-1-linux.png differ diff --git a/apps/web/playwright/snapshots/settings/room-settings/room-security-tab.spec.ts/room-security-settings-linux.png b/apps/web/playwright/snapshots/settings/room-settings/room-security-tab.spec.ts/room-security-settings-linux.png index 8e1db6f498..95657048ac 100644 Binary files a/apps/web/playwright/snapshots/settings/room-settings/room-security-tab.spec.ts/room-security-settings-linux.png and b/apps/web/playwright/snapshots/settings/room-settings/room-security-tab.spec.ts/room-security-settings-linux.png differ diff --git a/apps/web/playwright/snapshots/settings/room-settings/room-security-tab.spec.ts/room-security-settings-world-readable-linux.png b/apps/web/playwright/snapshots/settings/room-settings/room-security-tab.spec.ts/room-security-settings-world-readable-linux.png index ee764d5835..f5f3fa0382 100644 Binary files a/apps/web/playwright/snapshots/settings/room-settings/room-security-tab.spec.ts/room-security-settings-world-readable-linux.png and b/apps/web/playwright/snapshots/settings/room-settings/room-security-tab.spec.ts/room-security-settings-world-readable-linux.png differ diff --git a/apps/web/playwright/snapshots/timeline/timeline.spec.ts/long-strings-with-reply-bubble-layout-linux.png b/apps/web/playwright/snapshots/timeline/timeline.spec.ts/long-strings-with-reply-bubble-layout-linux.png index 0acbbbe6f6..9fe134e7f6 100644 Binary files a/apps/web/playwright/snapshots/timeline/timeline.spec.ts/long-strings-with-reply-bubble-layout-linux.png and b/apps/web/playwright/snapshots/timeline/timeline.spec.ts/long-strings-with-reply-bubble-layout-linux.png differ diff --git a/apps/web/playwright/snapshots/timeline/timeline.spec.ts/long-strings-with-reply-irc-layout-linux.png b/apps/web/playwright/snapshots/timeline/timeline.spec.ts/long-strings-with-reply-irc-layout-linux.png index e561564182..04e0a88ffa 100644 Binary files a/apps/web/playwright/snapshots/timeline/timeline.spec.ts/long-strings-with-reply-irc-layout-linux.png and b/apps/web/playwright/snapshots/timeline/timeline.spec.ts/long-strings-with-reply-irc-layout-linux.png differ diff --git a/apps/web/playwright/snapshots/timeline/timeline.spec.ts/long-strings-with-reply-modern-layout-linux.png b/apps/web/playwright/snapshots/timeline/timeline.spec.ts/long-strings-with-reply-modern-layout-linux.png index 01c094c6d0..311df4e505 100644 Binary files a/apps/web/playwright/snapshots/timeline/timeline.spec.ts/long-strings-with-reply-modern-layout-linux.png and b/apps/web/playwright/snapshots/timeline/timeline.spec.ts/long-strings-with-reply-modern-layout-linux.png differ diff --git a/apps/web/playwright/testcontainers/mas.ts b/apps/web/playwright/testcontainers/mas.ts index 45752fc10e..b38e59652a 100644 --- a/apps/web/playwright/testcontainers/mas.ts +++ b/apps/web/playwright/testcontainers/mas.ts @@ -11,7 +11,7 @@ import { } from "@element-hq/element-web-playwright-common/lib/testcontainers/index.js"; const DOCKER_IMAGE = - "ghcr.io/element-hq/matrix-authentication-service:main@sha256:f54c2214354ec3294694a525523debb5f38c8580c1a5afc8cdec0f8372374ef3"; + "ghcr.io/element-hq/matrix-authentication-service:main@sha256:b7bbeb4249bf4abfc86ccd6d1be60c3b68ccb41be407b2a50658f7ff53a44d80"; /** * MatrixAuthenticationServiceContainer which freezes the docker digest to diff --git a/apps/web/playwright/testcontainers/synapse.ts b/apps/web/playwright/testcontainers/synapse.ts index f67d51659a..d3d162f54b 100644 --- a/apps/web/playwright/testcontainers/synapse.ts +++ b/apps/web/playwright/testcontainers/synapse.ts @@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details. import { SynapseContainer as BaseSynapseContainer } from "@element-hq/element-web-playwright-common/lib/testcontainers/index.js"; const DOCKER_IMAGE = - "ghcr.io/element-hq/synapse:develop@sha256:73fe964d854412cd905f4f0e668b2a9d10edc86891036e03656714de0311f6f6"; + "ghcr.io/element-hq/synapse:develop@sha256:2ccad8f89462d119d910fbf91750477118a875a767d19e6387f750f8e8a9a5ae"; /** * SynapseContainer which freezes the docker digest to stabilise tests, diff --git a/apps/web/res/css/_components.pcss b/apps/web/res/css/_components.pcss index 1197cbabe6..bdca70276d 100644 --- a/apps/web/res/css/_components.pcss +++ b/apps/web/res/css/_components.pcss @@ -70,6 +70,7 @@ @import "./structures/_MatrixChat.pcss"; @import "./structures/_MessagePanel.pcss"; @import "./structures/_NonUrgentToastContainer.pcss"; +@import "./structures/_PictureInPictureDragger.pcss"; @import "./structures/_QuickSettingsButton.pcss"; @import "./structures/_RightPanel.pcss"; @import "./structures/_RoomSearch.pcss"; @@ -375,7 +376,6 @@ @import "./views/voip/_DialPad.pcss"; @import "./views/voip/_DialPadContextMenu.pcss"; @import "./views/voip/_DialPadModal.pcss"; -@import "./views/voip/_LegacyCallPreview.pcss"; @import "./views/voip/_LegacyCallView.pcss"; @import "./views/voip/_LegacyCallViewForRoom.pcss"; @import "./views/voip/_LegacyCallViewHeader.pcss"; diff --git a/apps/web/res/css/structures/_PictureInPictureDragger.pcss b/apps/web/res/css/structures/_PictureInPictureDragger.pcss new file mode 100644 index 0000000000..d6effd3b20 --- /dev/null +++ b/apps/web/res/css/structures/_PictureInPictureDragger.pcss @@ -0,0 +1,20 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +.mx_PictureInPictureDragger { + cursor: grab; + user-select: none; + left: 0; + position: fixed; + top: 0; + /* Display above any widget elements */ + z-index: 102; +} + +.mx_PictureInPictureDragger:active { + cursor: grabbing; +} diff --git a/apps/web/res/css/views/rooms/_EventTile.pcss b/apps/web/res/css/views/rooms/_EventTile.pcss index 5bea427961..63d1bdaee2 100644 --- a/apps/web/res/css/views/rooms/_EventTile.pcss +++ b/apps/web/res/css/views/rooms/_EventTile.pcss @@ -1378,6 +1378,10 @@ $left-gutter: 64px; display: flex; } +.mx_EventTile_annotatedInline { + display: inline-flex; +} + .mx_EventTile_footer { display: flex; gap: var(--cpd-space-2x); diff --git a/apps/web/res/css/views/settings/tabs/_SettingsSection.pcss b/apps/web/res/css/views/settings/tabs/_SettingsSection.pcss index ce3c9266c3..c98732e884 100644 --- a/apps/web/res/css/views/settings/tabs/_SettingsSection.pcss +++ b/apps/web/res/css/views/settings/tabs/_SettingsSection.pcss @@ -36,5 +36,5 @@ Please see LICENSE files in the repository root for full details. grid-template-columns: minmax(0, 1fr); gap: $spacing-32; - padding: $spacing-16 0; + margin: $spacing-16 0; } diff --git a/apps/web/res/css/views/voip/_LegacyCallPreview.pcss b/apps/web/res/css/views/voip/_LegacyCallPreview.pcss deleted file mode 100644 index 3a8bf5af9f..0000000000 --- a/apps/web/res/css/views/voip/_LegacyCallPreview.pcss +++ /dev/null @@ -1,28 +0,0 @@ -/* -Copyright 2024 New Vector Ltd. -Copyright 2021 Šimon Brandner - -SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial -Please see LICENSE files in the repository root for full details. -*/ - -.mx_LegacyCallPreview { - align-items: flex-end; - display: flex; - flex-direction: column; - gap: $spacing-16; - left: 0; - position: fixed; - top: 0; - /* Display above any widget elements */ - z-index: 102; - - .mx_VideoFeed_remote.mx_VideoFeed_voice { - min-height: 150px; - } - - .mx_VideoFeed_local { - border-radius: 8px; - overflow: hidden; - } -} diff --git a/apps/web/src/audio/VoiceRecording.ts b/apps/web/src/audio/VoiceRecording.ts index 705b96375a..44a016b995 100644 --- a/apps/web/src/audio/VoiceRecording.ts +++ b/apps/web/src/audio/VoiceRecording.ts @@ -103,10 +103,14 @@ export class VoiceRecording extends EventEmitter implements IDestroyable { private async makeRecorder(): Promise { try { + const requestedDeviceId = MediaDeviceHandler.getAudioInput(); + const deviceIdConstraint = + requestedDeviceId && requestedDeviceId !== "default" ? { deviceId: { exact: requestedDeviceId } } : {}; + this.recorderStream = await navigator.mediaDevices.getUserMedia({ audio: { channelCount: CHANNELS, - deviceId: MediaDeviceHandler.getAudioInput(), + ...deviceIdConstraint, autoGainControl: { ideal: MediaDeviceHandler.getAudioAutoGainControl() }, echoCancellation: { ideal: MediaDeviceHandler.getAudioEchoCancellation() }, noiseSuppression: { ideal: MediaDeviceHandler.getAudioNoiseSuppression() }, diff --git a/apps/web/src/components/structures/ContextMenu.tsx b/apps/web/src/components/structures/ContextMenu.tsx index 9d4b76e38e..3cd43b5d91 100644 --- a/apps/web/src/components/structures/ContextMenu.tsx +++ b/apps/web/src/components/structures/ContextMenu.tsx @@ -8,7 +8,15 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com Please see LICENSE files in the repository root for full details. */ -import React, { type JSX, type CSSProperties, type RefObject, type SyntheticEvent, useRef, useState } from "react"; +import React, { + type JSX, + type CSSProperties, + type RefObject, + type SyntheticEvent, + useRef, + useState, + type AriaRole, +} from "react"; import ReactDOM from "react-dom"; import classNames from "classnames"; import FocusLock from "react-focus-lock"; @@ -74,27 +82,31 @@ export interface MenuProps extends IPosition { export interface IProps extends MenuProps { // If true, insert an invisible screen-sized element behind the menu that when clicked will close it. - hasBackground?: boolean; + "hasBackground"?: boolean; // whether this context menu should be focus managed. If false it must handle itself - managed?: boolean; - wrapperClassName?: string; - menuClassName?: string; + "managed"?: boolean; + "wrapperClassName"?: string; + "menuClassName"?: string; // If true, this context menu will be mounted as a child to the parent container. Otherwise // it will be mounted to a container at the root of the DOM. - mountAsChild?: boolean; + "mountAsChild"?: boolean; // If specified, contents will be wrapped in a FocusLock, this is only needed if the context menu is being rendered // within an existing FocusLock e.g inside a modal. - focusLock?: boolean; + "focusLock"?: boolean; // call onFinished on any interaction with the menu - closeOnInteraction?: boolean; + "closeOnInteraction"?: boolean; // Function to be called on menu close onFinished(this: void): void; // on resize callback windowResize?(this: void): void; + + // Role & label for accessibility + "role"?: AriaRole; + "aria-label"?: string; } interface IState { @@ -257,9 +269,11 @@ export default class ContextMenu extends React.PureComponent {background} diff --git a/apps/web/src/components/structures/PictureInPictureDragger.tsx b/apps/web/src/components/structures/PictureInPictureDragger.tsx index 2cadc59a7b..d9f472c311 100644 --- a/apps/web/src/components/structures/PictureInPictureDragger.tsx +++ b/apps/web/src/components/structures/PictureInPictureDragger.tsx @@ -37,9 +37,7 @@ interface IChildrenOptions { } interface IProps { - className?: string; children: Array; - draggable: boolean; onDoubleClick?: () => void; onMove?: () => void; } @@ -181,9 +179,6 @@ export default class PictureInPictureDragger extends React.Component { }; private onStartMoving = (event: React.MouseEvent | MouseEvent): void => { - event.preventDefault(); - event.stopPropagation(); - this.mouseHeld = true; this.startingPositionX = event.clientX; this.startingPositionY = event.clientY; @@ -217,9 +212,6 @@ export default class PictureInPictureDragger extends React.Component { private onEndMoving = (event: MouseEvent): void => { if (!this.mouseHeld) return; - event.preventDefault(); - event.stopPropagation(); - this.mouseHeld = false; // Delaying this to the next event loop tick is necessary for click // event cancellation to work @@ -250,7 +242,7 @@ export default class PictureInPictureDragger extends React.Component { return (