Compare commits
203 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 013f1ce791 | |||
| b4af54a773 | |||
| d19208bee3 | |||
| faf3278a8e | |||
| fc47676c53 | |||
| 4db252fbb8 | |||
| b1254201db | |||
| d606c4966a | |||
| 35040ced82 | |||
| 6cda8c69be | |||
| 2d88cb451f | |||
| d6484a28c8 | |||
| a3b9a3aec1 | |||
| b933d16e5f | |||
| 33af62965a | |||
| 8a85efcfd6 | |||
| 55cc7ba597 | |||
| d6317b671f | |||
| 67fcbd1678 | |||
| 04ef3a2d5d | |||
| be843c4baa | |||
| d0b53a6a6c | |||
| 2a450e2520 | |||
| 4912c6e71b | |||
| 2ff5183806 | |||
| d684945877 | |||
| b3ca5c73c3 | |||
| bbaca2b390 | |||
| 226b260319 | |||
| 1af3ae70f8 | |||
| 68fc0123ac | |||
| 1c008df5c0 | |||
| 615a4e4385 | |||
| b3b6574638 | |||
| d7ca3dbd1d | |||
| 9aa0dab2a8 | |||
| 6937ca0a21 | |||
| ca2ea791b3 | |||
| 7fce635cc5 | |||
| 5094613180 | |||
| 2ee6933cfd | |||
| 838b3624b8 | |||
| f326814895 | |||
| 7eff3f6c68 | |||
| 6a7cbce2de | |||
| 9912b72535 | |||
| 892dcec448 | |||
| a643b727b5 | |||
| 23428c5bd6 | |||
| e1ec93d4bf | |||
| df11e1ff2b | |||
| 7e05552325 | |||
| 44fda3268a | |||
| f69c4df5aa | |||
| 7f31cf196f | |||
| 0692e120fa | |||
| 6ab88106c9 | |||
| ffd4270051 | |||
| 9360f0e5e2 | |||
| 2540c8a8af | |||
| 6613c3f87a | |||
| 858b6b6147 | |||
| d7f94f89dc | |||
| 8a21062476 | |||
| f6352afc6e | |||
| 0575ca9598 | |||
| 81b111371f | |||
| a76a0a1dd1 | |||
| 09884c6bd1 | |||
| 753e94f165 | |||
| 1b76f2b72c | |||
| d5b1bc7e65 | |||
| 563f6d338c | |||
| 562f7cc2bd | |||
| 6589e0b6df | |||
| 1053f99bda | |||
| c2f1793bb0 | |||
| de8ed98b53 | |||
| 77922d2217 | |||
| 242dc5ceb9 | |||
| abdb307279 | |||
| 56d9a5d93e | |||
| e8ddff0ad8 | |||
| 6da1412de8 | |||
| 6dba71a453 | |||
| 8de804d0c0 | |||
| 1d7fd0b9d3 | |||
| 00e1068758 | |||
| 2a8ea7a230 | |||
| b82c34d9d3 | |||
| 7738be32ec | |||
| a05b359c28 | |||
| 2a1708aa27 | |||
| bef626a61c | |||
| 323a325981 | |||
| c647c8ee3d | |||
| ea302162ee | |||
| b9de284c39 | |||
| 877ab183d9 | |||
| ae013686f5 | |||
| d54d082d21 | |||
| 2039a2a5bb | |||
| 679e8442a0 | |||
| d08ae5d30b | |||
| 79f7398d7c | |||
| 510365c96b | |||
| 34268cc29a | |||
| 31cfa204b6 | |||
| 4602ae8cb1 | |||
| 6269fb290b | |||
| 6d7def7b10 | |||
| a1be203683 | |||
| 6bc5036042 | |||
| 7d9086f767 | |||
| eb909f1090 | |||
| af55def428 | |||
| 92615272ce | |||
| 1b082a248c | |||
| 7c3cf696fd | |||
| 0053bedc38 | |||
| 674d64ac12 | |||
| 7dbffb348d | |||
| 25d24d478f | |||
| 62c7fe5408 | |||
| 8cef5df140 | |||
| 8381c435a1 | |||
| e07e26cae5 | |||
| 24018f7e94 | |||
| b31527261d | |||
| 8bb1cb5e63 | |||
| 8769165e88 | |||
| 4daf227137 | |||
| f0f93decfd | |||
| e5f0ea79e5 | |||
| b908e8c7f2 | |||
| dda87ff1ed | |||
| 617722018c | |||
| a972340216 | |||
| d5c49e6a2e | |||
| e0949e47c4 | |||
| d0c60e6ee4 | |||
| f36905b656 | |||
| d6f11d828b | |||
| b720a74eef | |||
| 3f26b37cf8 | |||
| e9da23517c | |||
| 28609f4e7a | |||
| 3f212537b6 | |||
| d6f3bd2063 | |||
| 7459850f6a | |||
| 0947517746 | |||
| 828c4a47a4 | |||
| 26779b10bd | |||
| c135c16824 | |||
| 88562127d7 | |||
| 6cfc441ae0 | |||
| 9da2f350dc | |||
| 19cfddab6d | |||
| 3fc3653809 | |||
| d733ac014c | |||
| 51f5ec021a | |||
| 0f6afca8e3 | |||
| 35fca4d339 | |||
| 4bddf37866 | |||
| b0e7cb4c1f | |||
| 77c4937f76 | |||
| e0b3e013ab | |||
| d6d647f56d | |||
| b9cdc0390a | |||
| a43dc3a3b5 | |||
| 64f719f553 | |||
| 9d1e36a37f | |||
| 2de53e306a | |||
| 6b48b022cb | |||
| ecb204a533 | |||
| de9a52d046 | |||
| edb63922e0 | |||
| 50d8fd2440 | |||
| d0c800a75c | |||
| bc6375d7ab | |||
| 819d361a91 | |||
| 2f44744f8b | |||
| a8b6d035d8 | |||
| 9edddce149 | |||
| d2d61a3203 | |||
| 905c1b9489 | |||
| 5783ee4cb8 | |||
| beeacf45ce | |||
| fd8fd0db70 | |||
| 7d64de7b2d | |||
| 22b506ff06 | |||
| 027047e0ae | |||
| d4c15d76f6 | |||
| 37d2e90812 | |||
| 0173041b17 | |||
| 9b778f0d52 | |||
| 18476096b7 | |||
| 26d7298ba4 | |||
| 006ad8868d | |||
| cba24e99da | |||
| 639ba793e2 | |||
| 76661d4cbe | |||
| bc4be12790 |
@@ -7,6 +7,7 @@ karma-reports/
|
||||
.idea/
|
||||
.tmp/
|
||||
config.json*
|
||||
.link-config
|
||||
# Exclude the playwright directory as much as we can as the snapshots are huge and we bind mount it in
|
||||
playwright/
|
||||
!playwright/docker-entrypoint.sh
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
src/vector/modernizr.js
|
||||
src/vector/modernizr.cjs
|
||||
test/end-to-end-tests/node_modules/
|
||||
test/end-to-end-tests/element/
|
||||
test/end-to-end-tests/synapse/
|
||||
@@ -11,3 +11,8 @@ src/modules.js
|
||||
# Test result files
|
||||
/playwright/test-results/
|
||||
/playwright/html-report/
|
||||
|
||||
# Shared components generated files
|
||||
/packages/shared-components/dist/
|
||||
/packages/shared-components/src/i18n/i18nKeys.d.ts
|
||||
/packages/shared-components/typedoc/
|
||||
|
||||
@@ -196,7 +196,7 @@ module.exports = {
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ["src/**/*.{ts,tsx}", "test/**/*.{ts,tsx}", "playwright/**/*.ts"],
|
||||
files: ["src/**/*.{ts,tsx}", "test/**/*.{ts,tsx}", "playwright/**/*.ts", "*.ts"],
|
||||
extends: ["plugin:matrix-org/typescript", "plugin:matrix-org/react"],
|
||||
rules: {
|
||||
"@typescript-eslint/explicit-function-return-type": [
|
||||
@@ -230,42 +230,6 @@ module.exports = {
|
||||
],
|
||||
},
|
||||
},
|
||||
// temporary override for offending icon require files
|
||||
{
|
||||
files: [
|
||||
"src/SdkConfig.ts",
|
||||
"src/components/structures/FileDropTarget.tsx",
|
||||
"src/components/structures/RoomStatusBar.tsx",
|
||||
"src/components/structures/UserMenu.tsx",
|
||||
"src/components/views/avatars/WidgetAvatar.tsx",
|
||||
"src/components/views/dialogs/AddExistingToSpaceDialog.tsx",
|
||||
"src/components/views/dialogs/ForwardDialog.tsx",
|
||||
"src/components/views/dialogs/InviteDialog.tsx",
|
||||
"src/components/views/dialogs/ModalWidgetDialog.tsx",
|
||||
"src/components/views/dialogs/UploadConfirmDialog.tsx",
|
||||
"src/components/views/dialogs/security/SetupEncryptionDialog.tsx",
|
||||
"src/components/views/elements/AddressTile.tsx",
|
||||
"src/components/views/elements/AppWarning.tsx",
|
||||
"src/components/views/elements/SSOButtons.tsx",
|
||||
"src/components/views/messages/MAudioBody.tsx",
|
||||
"src/components/views/messages/MImageBody.tsx",
|
||||
"src/components/views/messages/MFileBody.tsx",
|
||||
"src/components/views/messages/MStickerBody.tsx",
|
||||
"src/components/views/messages/MVideoBody.tsx",
|
||||
"src/components/views/messages/MVoiceMessageBody.tsx",
|
||||
"src/components/views/right_panel/EncryptionPanel.tsx",
|
||||
"src/components/views/rooms/EntityTile.tsx",
|
||||
"src/components/views/rooms/LinkPreviewGroup.tsx",
|
||||
"src/components/views/rooms/MemberList.tsx",
|
||||
"src/components/views/rooms/MessageComposer.tsx",
|
||||
"src/components/views/rooms/ReplyPreview.tsx",
|
||||
"src/components/views/settings/tabs/room/SecurityRoomSettingsTab.tsx",
|
||||
"src/components/views/settings/tabs/user/GeneralUserSettingsTab.tsx",
|
||||
],
|
||||
rules: {
|
||||
"@typescript-eslint/no-var-requires": "off",
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["test/**/*.{ts,tsx}", "playwright/**/*.ts"],
|
||||
extends: ["plugin:matrix-org/jest"],
|
||||
@@ -1,7 +1,7 @@
|
||||
* @element-hq/element-web-reviewers
|
||||
/.github/workflows/** @element-hq/element-web-team
|
||||
/package.json @element-hq/element-web-team
|
||||
/yarn.lock @element-hq/element-web-team
|
||||
/pnpm-lock.yaml @element-hq/element-web-team
|
||||
|
||||
/src/SecurityManager.ts @element-hq/element-crypto-web-reviewers
|
||||
/test/SecurityManager-test.ts @element-hq/element-crypto-web-reviewers
|
||||
@@ -16,6 +16,7 @@
|
||||
/src/components/views/dialogs/devtools/Crypto.tsx @element-hq/element-crypto-web-reviewers
|
||||
/playwright/e2e/crypto/ @element-hq/element-crypto-web-reviewers
|
||||
/playwright/e2e/settings/encryption-user-tab/ @element-hq/element-crypto-web-reviewers
|
||||
/packages/shared-components/src/crypto/ @element-hq/element-crypto-web-reviewers
|
||||
|
||||
|
||||
/src/models/Call.ts @element-hq/element-call-reviewers
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
name: Bug report for the Element desktop app (not in a browser)
|
||||
description: File a bug report if you are using the desktop Element application.
|
||||
labels: [T-Defect]
|
||||
labels: [T-Defect, A-Electron]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
|
||||
@@ -3,3 +3,6 @@ contact_links:
|
||||
- name: Questions & support
|
||||
url: https://matrix.to/#/#element-web:matrix.org
|
||||
about: Please ask and answer questions here.
|
||||
- name: Bug report for the Element flatpak app
|
||||
url: https://github.com/flathub/im.riot.Riot/issues
|
||||
about: Please file bugs with the Flatpak application on the respective repository.
|
||||
|
||||
@@ -173,6 +173,12 @@
|
||||
color: "bfd4f2"
|
||||
- name: "A-Welcome-Page"
|
||||
color: "bfd4f2"
|
||||
- name: "A-Install"
|
||||
color: "72A447"
|
||||
- name: "A-Seshat"
|
||||
color: "8262BE"
|
||||
- name: "A-Update"
|
||||
color: "17BE67"
|
||||
- name: "backport staging"
|
||||
description: "Label to automatically backport PR to staging branch"
|
||||
color: "B60205"
|
||||
@@ -282,3 +288,23 @@
|
||||
- name: "Z-Skip-Coverage"
|
||||
description: "Skip SonarQube coverage for this PR"
|
||||
color: "ededed"
|
||||
- name: "Z-Arch"
|
||||
color: "D601BE"
|
||||
- name: "Z-ARM"
|
||||
color: "5DEC5B"
|
||||
- name: "Z-Flatpak"
|
||||
color: "0CA856"
|
||||
- name: "Z-Linux"
|
||||
color: "7B4A9C"
|
||||
- name: "Z-macOS"
|
||||
color: "500605"
|
||||
- name: "Z-Official"
|
||||
color: "1D2B20"
|
||||
- name: "Z-Snap"
|
||||
color: "29CD95"
|
||||
- name: "Z-Suse"
|
||||
color: "79D07B"
|
||||
- name: "Z-Wayland"
|
||||
color: "94C519"
|
||||
- name: "Z-Windows"
|
||||
color: "0632DE"
|
||||
|
||||
@@ -1,4 +1,15 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": ["github>matrix-org/renovate-config-element-web"]
|
||||
"extends": ["github>matrix-org/renovate-config-element-web"],
|
||||
"postUpdateOptions": ["pnpmDedupe"],
|
||||
"customManagers": [
|
||||
{
|
||||
"customType": "regex",
|
||||
"datasourceTemplate": "docker",
|
||||
"versioningTemplate": "loose",
|
||||
"description": "Update testcontainers docker digests",
|
||||
"managerFilePatterns": ["**/testcontainers/*.ts"],
|
||||
"matchStrings": ["\\s+\"(?<depName>[^@]+):(?<currentValue>[^@]+)@(?<currentDigest>sha256:[a-f0-9]+)\""]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ concurrency:
|
||||
env:
|
||||
# This must be set for fetchdep.sh to get the right branch
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
NX_DEFAULT_OUTPUT_STYLE: stream-without-prefixes
|
||||
permissions: {} # No permissions required
|
||||
jobs:
|
||||
build:
|
||||
@@ -44,16 +45,14 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
||||
|
||||
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
||||
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
with:
|
||||
# Disable cache on Windows as it is slower than not caching
|
||||
# https://github.com/actions/setup-node/issues/975
|
||||
cache: ${{ runner.os != 'Windows' && 'yarn' || '' }}
|
||||
cache: ${{ runner.os != 'Windows' && 'pnpm' || '' }}
|
||||
node-version: "lts/*"
|
||||
|
||||
# Workaround for yarn install timeouts, especially on Windows
|
||||
- run: yarn config set network-timeout 300000
|
||||
|
||||
- name: Fetch layered build
|
||||
run: ./scripts/layered.sh
|
||||
|
||||
@@ -63,7 +62,7 @@ jobs:
|
||||
- name: Build
|
||||
env:
|
||||
CI_PACKAGE: true
|
||||
run: VERSION=$(scripts/get-version-from-git.sh) yarn build
|
||||
run: VERSION=$(scripts/get-version-from-git.sh) pnpm build
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
||||
|
||||
@@ -9,6 +9,8 @@ on:
|
||||
concurrency:
|
||||
group: ${{ github.repository_owner }}-${{ github.workflow }}-${{ github.ref_name }}
|
||||
cancel-in-progress: true
|
||||
env:
|
||||
NX_DEFAULT_OUTPUT_STYLE: stream-without-prefixes
|
||||
permissions: {}
|
||||
jobs:
|
||||
build:
|
||||
@@ -28,9 +30,10 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
||||
|
||||
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
||||
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
with:
|
||||
cache: "yarn"
|
||||
cache: "pnpm"
|
||||
node-version: "lts/*"
|
||||
|
||||
- name: Install Dependencies
|
||||
|
||||
@@ -95,14 +95,14 @@ jobs:
|
||||
latest=${{ contains(github.ref_name, '-rc.') && 'false' || 'auto' }}
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3
|
||||
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
|
||||
if: github.event_name != 'pull_request'
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3
|
||||
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
|
||||
if: github.event_name != 'pull_request'
|
||||
with:
|
||||
registry: ghcr.io
|
||||
@@ -138,7 +138,7 @@ jobs:
|
||||
services/web-repositories/secret/data/oci.element.io password | OCI_PASSWORD ;
|
||||
|
||||
- name: Login to oci.element.io Registry
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3
|
||||
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
|
||||
if: github.event_name != 'pull_request'
|
||||
with:
|
||||
registry: oci-push.vpn.infra.element.io
|
||||
|
||||
@@ -33,17 +33,20 @@ jobs:
|
||||
repository: matrix-org/matrix-js-sdk
|
||||
path: matrix-js-sdk
|
||||
|
||||
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
||||
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
|
||||
with:
|
||||
cache: "yarn"
|
||||
cache-dependency-path: element-web/yarn.lock
|
||||
package_json_file: element-web/package.json
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
with:
|
||||
cache: "pnpm"
|
||||
cache-dependency-path: element-web/pnpm-lock.yaml
|
||||
node-version: "lts/*"
|
||||
|
||||
- name: Generate automations docs
|
||||
working-directory: element-web
|
||||
run: |
|
||||
yarn install --frozen-lockfile
|
||||
yarn node ./scripts/gen-workflow-mermaid.ts ../element-desktop ../element-web ../matrix-js-sdk > docs/automations.md
|
||||
pnpm install --frozen-lockfile
|
||||
pnpm node ./scripts/gen-workflow-mermaid.ts ../element-desktop ../element-web ../matrix-js-sdk > docs/automations.md
|
||||
echo "- [Automations](automations.md)" >> docs/SUMMARY.md
|
||||
|
||||
- name: Setup mdBook
|
||||
|
||||
@@ -37,6 +37,7 @@ env:
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
# Use 6 runners in the default case, but 4 when running on a schedule where we run all 5 projects (20 runners total)
|
||||
NUM_RUNNERS: ${{ github.event_name == 'schedule' && 4 || 6 }}
|
||||
NX_DEFAULT_OUTPUT_STYLE: stream-without-prefixes
|
||||
|
||||
permissions: {} # No permissions required
|
||||
|
||||
@@ -54,9 +55,10 @@ jobs:
|
||||
with:
|
||||
repository: element-hq/element-web
|
||||
|
||||
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
||||
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
with:
|
||||
cache: "yarn"
|
||||
cache: "pnpm"
|
||||
node-version: "lts/*"
|
||||
|
||||
- name: Fetch layered build
|
||||
@@ -71,7 +73,7 @@ jobs:
|
||||
- name: Build
|
||||
env:
|
||||
CI_PACKAGE: true
|
||||
run: VERSION=$(scripts/get-version-from-git.sh) yarn build
|
||||
run: VERSION=$(scripts/get-version-from-git.sh) pnpm build
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
||||
@@ -133,21 +135,22 @@ jobs:
|
||||
name: webapp
|
||||
path: webapp
|
||||
|
||||
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
||||
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
with:
|
||||
cache: "yarn"
|
||||
cache-dependency-path: yarn.lock
|
||||
cache: "pnpm"
|
||||
cache-dependency-path: pnpm-lock.yaml
|
||||
node-version: "lts/*"
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Get installed Playwright version
|
||||
id: playwright
|
||||
run: echo "version=$(yarn list --pattern @playwright/test --depth=0 --json --non-interactive --no-progress | jq -r '.data.trees[].name')" >> $GITHUB_OUTPUT
|
||||
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@9255dc7a253b0ccc959486e2bca901246202afeb # v5
|
||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5
|
||||
id: playwright-cache
|
||||
with:
|
||||
path: ~/.cache/ms-playwright
|
||||
@@ -155,17 +158,17 @@ jobs:
|
||||
|
||||
- name: Install Playwright browsers
|
||||
if: steps.playwright-cache.outputs.cache-hit != 'true'
|
||||
run: yarn playwright install --with-deps --no-shell
|
||||
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'
|
||||
run: yarn playwright install-deps webkit
|
||||
run: pnpm playwright install-deps webkit
|
||||
|
||||
# We skip tests tagged with @mergequeue when running on PRs, but run them in MQ and everywhere else
|
||||
- name: Run Playwright tests
|
||||
run: |
|
||||
yarn playwright test \
|
||||
pnpm playwright test \
|
||||
--shard "${{ matrix.runner }}/${{ needs.build.outputs.num-runners }}" \
|
||||
--project="${{ matrix.project }}" \
|
||||
${{ (github.event_name == 'pull_request' && matrix.runAllTests == false ) && '--grep-invert @mergequeue' || '' }}
|
||||
@@ -200,15 +203,17 @@ jobs:
|
||||
persist-credentials: false
|
||||
repository: element-hq/element-web
|
||||
|
||||
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
||||
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
|
||||
if: inputs.skip != true
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
if: inputs.skip != true
|
||||
with:
|
||||
cache: "yarn"
|
||||
cache: "pnpm"
|
||||
node-version: "lts/*"
|
||||
|
||||
- name: Install dependencies
|
||||
if: inputs.skip != true
|
||||
run: yarn install --frozen-lockfile
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Download blob reports from GitHub Actions Artifacts
|
||||
if: inputs.skip != true
|
||||
@@ -220,7 +225,7 @@ jobs:
|
||||
|
||||
- name: Merge into HTML Report
|
||||
if: inputs.skip != true
|
||||
run: yarn playwright merge-reports --reporter=html,./playwright/flaky-reporter.ts,@element-hq/element-web-playwright-common/lib/stale-screenshot-reporter.js ./all-blob-reports
|
||||
run: pnpm playwright merge-reports --reporter=html,./playwright/flaky-reporter.ts,@element-hq/element-web-playwright-common/lib/stale-screenshot-reporter.js ./all-blob-reports
|
||||
env:
|
||||
# Only pass creds to the flaky-reporter on main branch runs
|
||||
GITHUB_TOKEN: ${{ github.ref_name == 'develop' && secrets.ELEMENT_BOT_TOKEN || '' }}
|
||||
|
||||
@@ -8,5 +8,7 @@ permissions:
|
||||
jobs:
|
||||
download:
|
||||
uses: matrix-org/matrix-web-i18n/.github/workflows/localazy_download.yaml@main
|
||||
with:
|
||||
packageManager: pnpm
|
||||
secrets:
|
||||
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
@@ -4,6 +4,7 @@ on:
|
||||
branches: [develop]
|
||||
paths:
|
||||
- "src/i18n/strings/en_EN.json"
|
||||
- "packages/shared-components/src/i18n/strings/en_EN.json"
|
||||
permissions: {} # No permissions needed
|
||||
jobs:
|
||||
upload:
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
name: Update Playwright docker images
|
||||
on:
|
||||
workflow_dispatch: {}
|
||||
schedule:
|
||||
- cron: "0 6 * * *" # Every day at 6am UTC
|
||||
permissions: {}
|
||||
jobs:
|
||||
update:
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
||||
|
||||
- name: Update synapse image
|
||||
run: |
|
||||
docker pull "$IMAGE"
|
||||
INSPECT=$(docker inspect --format='{{index .RepoDigests 0}}' "$IMAGE")
|
||||
DIGEST=${INSPECT#*@}
|
||||
sed -i "s/const TAG.*/const TAG = \"develop@$DIGEST\";/" playwright/testcontainers/synapse.ts
|
||||
env:
|
||||
IMAGE: ghcr.io/element-hq/synapse:develop
|
||||
|
||||
- name: Update MAS image
|
||||
run: |
|
||||
docker pull "$IMAGE"
|
||||
INSPECT=$(docker inspect --format='{{index .RepoDigests 0}}' "$IMAGE")
|
||||
DIGEST=${INSPECT#*@}
|
||||
sed -i "s/const TAG.*/const TAG = \"main@$DIGEST\";/" playwright/testcontainers/mas.ts
|
||||
env:
|
||||
IMAGE: ghcr.io/element-hq/matrix-authentication-service:main
|
||||
|
||||
- name: Create Pull Request
|
||||
id: cpr
|
||||
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # v8
|
||||
with:
|
||||
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
branch: actions/playwright-image-updates
|
||||
delete-branch: true
|
||||
title: Playwright Docker image updates
|
||||
labels: |
|
||||
T-Task
|
||||
|
||||
- name: Enable automerge
|
||||
run: gh pr merge --merge --auto "$PR_NUMBER"
|
||||
if: steps.cpr.outputs.pull-request-operation == 'created'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
PR_NUMBER: ${{ steps.cpr.outputs.pull-request-number }}
|
||||
|
||||
- name: Enable autoapprove
|
||||
run: |
|
||||
gh pr review --approve "$PR_NUMBER"
|
||||
if: steps.cpr.outputs.pull-request-operation == 'created'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PR_NUMBER: ${{ steps.cpr.outputs.pull-request-number }}
|
||||
@@ -15,10 +15,11 @@ jobs:
|
||||
- name: 🧮 Checkout code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
||||
|
||||
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
|
||||
- name: 🔧 Set up node environment
|
||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
with:
|
||||
cache: "yarn"
|
||||
cache: "pnpm"
|
||||
node-version-file: ".node-version"
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
|
||||
@@ -28,13 +29,8 @@ jobs:
|
||||
|
||||
# Need to setup element web too as it needs the translations
|
||||
- name: 🛠️ Setup EW
|
||||
run: yarn install --pure-lockfile
|
||||
|
||||
- name: 🛠️ Setup
|
||||
# When running `install` it also calls the `prepare` step which generates
|
||||
# a build
|
||||
run: yarn --cwd packages/shared-components install --pure-lockfile
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: 🚀 Publish to npm
|
||||
working-directory: packages/shared-components
|
||||
run: npm publish --access public --tag test --provenance
|
||||
run: npm publish --access public --provenance
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
name: Build shared component storybook
|
||||
on:
|
||||
merge_group: {}
|
||||
pull_request: {}
|
||||
workflow_call: {}
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
doc:
|
||||
name: Build storybook
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 🧮 Checkout code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
||||
|
||||
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
|
||||
- name: 🔧 Pnpm cache
|
||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
||||
with:
|
||||
cache: "pnpm"
|
||||
node-version-file: package.json
|
||||
|
||||
- name: 🔨 Install dependencies
|
||||
working-directory: packages/shared-components
|
||||
run: "pnpm install --frozen-lockfile"
|
||||
|
||||
- name: 📖 Build Storybook
|
||||
working-directory: packages/shared-components
|
||||
run: pnpm build:storybook
|
||||
|
||||
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
||||
with:
|
||||
name: shared-components-storybook
|
||||
path: packages/shared-components/storybook-static
|
||||
retention-days: 1
|
||||
@@ -0,0 +1,33 @@
|
||||
name: Publish shared component storybook
|
||||
on:
|
||||
workflow_dispatch: {}
|
||||
push:
|
||||
branches:
|
||||
- "develop"
|
||||
paths:
|
||||
- "packages/shared-components/**/*"
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build storybook
|
||||
uses: ./.github/workflows/shared-component-storybook-build.yml
|
||||
|
||||
publish:
|
||||
name: Publish storybook
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
environment: SharedComponents
|
||||
steps:
|
||||
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
|
||||
with:
|
||||
name: shared-components-storybook
|
||||
path: storybook-static
|
||||
|
||||
- name: 🚀 Deploy to Cloudflare Pages
|
||||
uses: cloudflare/wrangler-action@9681c2997648301493e78cacbfb790a9f19c833f # v3
|
||||
with:
|
||||
apiToken: ${{ secrets.CF_PAGES_TOKEN }}
|
||||
accountId: ${{ secrets.CF_PAGES_ACCOUNT_ID }}
|
||||
command: pages deploy storybook-static --project-name=shared-components-storybook
|
||||
@@ -26,25 +26,23 @@ jobs:
|
||||
persist-credentials: false
|
||||
repository: element-hq/element-web
|
||||
|
||||
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
||||
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
with:
|
||||
cache: "yarn"
|
||||
cache: "pnpm"
|
||||
node-version: "lts/*"
|
||||
|
||||
- name: Install element web dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Install dependencies
|
||||
working-directory: packages/shared-components
|
||||
run: yarn install --frozen-lockfile
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Get installed Playwright version
|
||||
working-directory: packages/shared-components
|
||||
id: playwright
|
||||
run: echo "version=$(yarn list --pattern @playwright/test --depth=0 --json --non-interactive --no-progress | jq -r '.data.trees[].name')" >> $GITHUB_OUTPUT
|
||||
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@9255dc7a253b0ccc959486e2bca901246202afeb # v5
|
||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5
|
||||
id: playwright-cache
|
||||
with:
|
||||
path: ~/.cache/ms-playwright
|
||||
@@ -53,14 +51,21 @@ jobs:
|
||||
- name: Install Playwright browsers
|
||||
working-directory: packages/shared-components
|
||||
if: steps.playwright-cache.outputs.cache-hit != 'true'
|
||||
run: "yarn playwright install --with-deps --only-shell"
|
||||
run: "pnpm playwright install --with-deps --only-shell"
|
||||
|
||||
- name: Run Visual tests
|
||||
run: "yarn --cwd packages/shared-components test:storybook:ci"
|
||||
working-directory: packages/shared-components
|
||||
run: "pnpm test:storybook --run"
|
||||
|
||||
# Workaround for vis silently adding new baselines if they didn't exist
|
||||
# Can be removed once https://github.com/repobuddy/visual-testing/issues/516 is released
|
||||
- run: |
|
||||
git add -N .
|
||||
git diff --exit-code
|
||||
|
||||
- name: Upload received images & diffs
|
||||
if: always()
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
||||
with:
|
||||
name: received-images
|
||||
path: packages/shared-components/playwright/shared-component-received
|
||||
path: packages/shared-components/__vis__/linux
|
||||
|
||||
@@ -14,6 +14,7 @@ concurrency:
|
||||
env:
|
||||
# This must be set for fetchdep.sh to get the right branch
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
NX_DEFAULT_OUTPUT_STYLE: stream-without-prefixes
|
||||
|
||||
permissions: {} # No permissions required
|
||||
|
||||
@@ -24,30 +25,29 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
||||
|
||||
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
||||
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
with:
|
||||
cache: "yarn"
|
||||
cache: "pnpm"
|
||||
node-version: "lts/*"
|
||||
|
||||
- name: Install Dependencies
|
||||
run: "./scripts/layered.sh"
|
||||
|
||||
- name: Typecheck
|
||||
run: "yarn run lint:types"
|
||||
|
||||
- name: Install Shared Component Dependencies
|
||||
run: "yarn --cwd packages/shared-components install"
|
||||
run: "pnpm run lint:types"
|
||||
|
||||
- name: Typecheck Shared Components
|
||||
run: "yarn --cwd packages/shared-components run lint:types"
|
||||
run: "pnpm --dir packages/shared-components run lint:types"
|
||||
|
||||
i18n_lint_ew:
|
||||
name: "i18n Check (Element Web)"
|
||||
uses: matrix-org/matrix-web-i18n/.github/workflows/i18n_check.yml@main
|
||||
uses: matrix-org/matrix-web-i18n/.github/workflows/i18n_check.yml@3673fd3abbf8dfae1de849c6cd3e69e24ed7a766
|
||||
permissions:
|
||||
pull-requests: read
|
||||
with:
|
||||
hardcoded-words: "Element"
|
||||
packageManager: pnpm
|
||||
allowed-hardcoded-keys: |
|
||||
console_dev_note
|
||||
labs|element_call_video_rooms
|
||||
@@ -58,15 +58,18 @@ jobs:
|
||||
welcome_to_element
|
||||
devtools|settings|elementCallUrl
|
||||
labs|sliding_sync_description
|
||||
settings|voip|noise_suppression_description
|
||||
settings|voip|echo_cancellation_description
|
||||
|
||||
i18n_lint_shared_components:
|
||||
name: "i18n Check (Shared Components)"
|
||||
uses: matrix-org/matrix-web-i18n/.github/workflows/i18n_check.yml@main
|
||||
uses: matrix-org/matrix-web-i18n/.github/workflows/i18n_check.yml@3673fd3abbf8dfae1de849c6cd3e69e24ed7a766
|
||||
permissions:
|
||||
pull-requests: read
|
||||
with:
|
||||
path: "packages/shared-components"
|
||||
hardcoded-words: "Element"
|
||||
packageManager: pnpm
|
||||
|
||||
rethemendex_lint:
|
||||
name: "Rethemendex Check"
|
||||
@@ -84,23 +87,21 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
||||
|
||||
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
||||
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
with:
|
||||
cache: "yarn"
|
||||
cache: "pnpm"
|
||||
node-version: "lts/*"
|
||||
|
||||
# Does not need branch matching as only analyses this layer
|
||||
- name: Install Deps
|
||||
run: "yarn install --frozen-lockfile"
|
||||
run: "pnpm install --frozen-lockfile"
|
||||
|
||||
- name: Run Linter
|
||||
run: "yarn run lint:js"
|
||||
|
||||
- name: Install Shared Component Deps
|
||||
run: "yarn --cwd packages/shared-components install --frozen-lockfile"
|
||||
run: "pnpm run lint:js"
|
||||
|
||||
- name: Run Linter
|
||||
run: "yarn --cwd packages/shared-components run lint:js"
|
||||
run: "pnpm --dir packages/shared-components run lint:js"
|
||||
|
||||
style_lint:
|
||||
name: "Style Lint"
|
||||
@@ -108,17 +109,18 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
||||
|
||||
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
||||
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
with:
|
||||
cache: "yarn"
|
||||
cache: "pnpm"
|
||||
node-version: "lts/*"
|
||||
|
||||
# Does not need branch matching as only analyses this layer
|
||||
- name: Install Deps
|
||||
run: "yarn install"
|
||||
run: "pnpm install"
|
||||
|
||||
- name: Run Linter
|
||||
run: "yarn run lint:style"
|
||||
run: "pnpm run lint:style"
|
||||
|
||||
workflow_lint:
|
||||
name: "Workflow Lint"
|
||||
@@ -126,17 +128,18 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
||||
|
||||
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
||||
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
with:
|
||||
cache: "yarn"
|
||||
cache: "pnpm"
|
||||
node-version: "lts/*"
|
||||
|
||||
# Does not need branch matching as only analyses this layer
|
||||
- name: Install Deps
|
||||
run: "yarn install --frozen-lockfile"
|
||||
run: "pnpm install --frozen-lockfile"
|
||||
|
||||
- name: Run Linter
|
||||
run: "yarn lint:workflows"
|
||||
run: "pnpm lint:workflows"
|
||||
|
||||
analyse_dead_code:
|
||||
name: "Analyse Dead Code"
|
||||
@@ -144,13 +147,14 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
||||
|
||||
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
||||
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
with:
|
||||
cache: "yarn"
|
||||
cache: "pnpm"
|
||||
node-version: "lts/*"
|
||||
|
||||
- name: Install Deps
|
||||
run: "yarn install --frozen-lockfile"
|
||||
run: "pnpm install --frozen-lockfile"
|
||||
|
||||
- name: Run linter
|
||||
run: "yarn run lint:knip"
|
||||
run: "pnpm run lint:knip"
|
||||
|
||||
@@ -25,6 +25,7 @@ env:
|
||||
ENABLE_COVERAGE: ${{ github.event_name != 'merge_group' && inputs.disable_coverage != 'true' }}
|
||||
# fetchdep.sh needs to know our PR number
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
NX_DEFAULT_OUTPUT_STYLE: stream-without-prefixes
|
||||
|
||||
permissions: {}
|
||||
|
||||
@@ -43,11 +44,12 @@ jobs:
|
||||
with:
|
||||
repository: ${{ inputs.matrix-js-sdk-sha && 'element-hq/element-web' || github.repository }}
|
||||
|
||||
- name: Yarn cache
|
||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
||||
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
|
||||
- name: pnpm cache
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
with:
|
||||
node-version: "lts/*"
|
||||
cache: "yarn"
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Install Deps
|
||||
run: "./scripts/layered.sh"
|
||||
@@ -55,10 +57,10 @@ jobs:
|
||||
JS_SDK_GITHUB_BASE_REF: ${{ inputs.matrix-js-sdk-sha }}
|
||||
|
||||
- name: Jest Cache
|
||||
uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5
|
||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5
|
||||
with:
|
||||
path: /tmp/jest_cache
|
||||
key: ${{ hashFiles('**/yarn.lock') }}
|
||||
key: ${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
- name: Get number of CPU cores
|
||||
id: cpu-cores
|
||||
@@ -66,7 +68,7 @@ jobs:
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
yarn test \
|
||||
pnpm test \
|
||||
--coverage=${{ env.ENABLE_COVERAGE }} \
|
||||
--ci \
|
||||
--max-workers ${{ steps.cpu-cores.outputs.count }} \
|
||||
@@ -93,18 +95,18 @@ jobs:
|
||||
|
||||
complete:
|
||||
name: jest-tests
|
||||
needs: jest_ew
|
||||
needs: [jest_ew, vitest_sc]
|
||||
if: always()
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
statuses: write
|
||||
steps:
|
||||
- if: needs.jest_ew.result != 'skipped' && needs.jest_ew.result != 'success'
|
||||
- if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')
|
||||
run: exit 1
|
||||
|
||||
- name: Skip SonarCloud in merge queue
|
||||
if: github.event_name == 'merge_group' || inputs.disable_coverage == 'true'
|
||||
uses: guibranco/github-status-action-v2@5530c593759f489bba08272e96986ffc571c1ea1
|
||||
uses: guibranco/github-status-action-v2@9bfa8773cdbdc6c185747fd43cd7faa9d7c32f09
|
||||
with:
|
||||
authToken: ${{ secrets.GITHUB_TOKEN }}
|
||||
state: success
|
||||
@@ -113,8 +115,8 @@ jobs:
|
||||
sha: ${{ github.sha }}
|
||||
target_url: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
|
||||
jest_sc:
|
||||
name: Jest (Shared Components)
|
||||
vitest_sc:
|
||||
name: Vitest (Shared Components)
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
@@ -122,40 +124,45 @@ jobs:
|
||||
with:
|
||||
repository: ${{ inputs.matrix-js-sdk-sha && 'element-hq/element-web' || github.repository }}
|
||||
|
||||
- name: Yarn cache
|
||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
||||
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
|
||||
- name: pnpm cache
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
with:
|
||||
node-version: "lts/*"
|
||||
cache: "yarn"
|
||||
|
||||
- name: Install EW Deps
|
||||
run: "yarn install"
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Install Shared Component Deps
|
||||
working-directory: "packages/shared-components"
|
||||
run: "yarn install"
|
||||
run: "pnpm install"
|
||||
|
||||
- name: Jest Cache
|
||||
uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5
|
||||
- name: Cache storybook & vitest
|
||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5
|
||||
with:
|
||||
path: /tmp/jest_cache
|
||||
key: ${{ hashFiles('**/yarn.lock') }}
|
||||
path: |
|
||||
packages/shared-components/node_modules/.cache
|
||||
packages/shared-components/node_modules/.vite/vitest
|
||||
key: ${{ hashFiles('pnpm-lock.yaml') }}
|
||||
|
||||
- name: Get number of CPU cores
|
||||
id: cpu-cores
|
||||
uses: SimenB/github-actions-cpu-cores@97ba232459a8e02ff6121db9362b09661c875ab8 # v2
|
||||
- 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@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5
|
||||
id: playwright-cache
|
||||
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"
|
||||
|
||||
- name: Run tests
|
||||
working-directory: "packages/shared-components"
|
||||
run: |
|
||||
yarn test \
|
||||
--coverage=${{ env.ENABLE_COVERAGE }} \
|
||||
--ci \
|
||||
--max-workers ${{ steps.cpu-cores.outputs.count }} \
|
||||
--cacheDirectory /tmp/jest_cache
|
||||
env:
|
||||
# tell jest to use coloured output
|
||||
FORCE_COLOR: true
|
||||
run: pnpm test:unit --coverage=${{ env.ENABLE_COVERAGE }}
|
||||
|
||||
- name: Upload Artifact
|
||||
if: env.ENABLE_COVERAGE == 'true'
|
||||
|
||||
@@ -11,19 +11,20 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
||||
|
||||
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
||||
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
with:
|
||||
cache: "yarn"
|
||||
cache: "pnpm"
|
||||
node-version: "lts/*"
|
||||
|
||||
- name: Install Deps
|
||||
run: "yarn install --frozen-lockfile"
|
||||
run: "pnpm install --frozen-lockfile"
|
||||
|
||||
- name: Fetch Jitsi
|
||||
run: "yarn update:jitsi"
|
||||
run: "pnpm vendor:jitsi"
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # v8
|
||||
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8
|
||||
with:
|
||||
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
branch: actions/jitsi-update
|
||||
|
||||
@@ -30,6 +30,7 @@ electron/pub
|
||||
/index.html
|
||||
# version file and tarball created by `npm pack` / `yarn pack`
|
||||
/git-revision.txt
|
||||
jest-sonar.xml
|
||||
|
||||
*storybook.log
|
||||
storybook-static
|
||||
@@ -37,3 +38,13 @@ storybook-static
|
||||
/packages/shared-components/node_modules
|
||||
/packages/shared-components/dist
|
||||
/packages/shared-components/src/i18nKeys.d.ts
|
||||
|
||||
# TSC incremental compilation information
|
||||
*.tsbuildinfo
|
||||
|
||||
/.link-config
|
||||
|
||||
.nx/cache
|
||||
.nx/workspace-data
|
||||
|
||||
.pnpm-store
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
/node_modules
|
||||
/webapp
|
||||
/*.log
|
||||
yarn.lock
|
||||
pnpm-lock.yaml
|
||||
electron/dist
|
||||
electron/pub
|
||||
**/.idea
|
||||
@@ -21,7 +21,7 @@ electron/pub
|
||||
/build_config.yaml
|
||||
# Raises an error because it contains a template var breaking the script tag
|
||||
src/vector/index.html
|
||||
src/vector/modernizr.js
|
||||
src/vector/modernizr.cjs
|
||||
/docs/lib
|
||||
/book
|
||||
/debian/tmp
|
||||
@@ -44,3 +44,9 @@ res/jitsi_external_api.min.js
|
||||
/playwright/html-report/
|
||||
/playwright/logs/
|
||||
/playwright/snapshots/
|
||||
|
||||
# Shared components generated files
|
||||
packages/shared-components/dist/
|
||||
packages/shared-components/src/i18n/i18nKeys.d.ts
|
||||
packages/shared-components/typedoc/
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
module.exports = require("eslint-plugin-matrix-org/.prettierrc.js");
|
||||
@@ -56,7 +56,6 @@ module.exports = {
|
||||
{ from: "res/css/views/rooms/_EditMessageComposer.pcss", type: "css" },
|
||||
{ from: "res/css/views/right_panel/_BaseCard.pcss", type: "css" },
|
||||
{ from: "res/css/views/messages/_MessageTimestamp.pcss", type: "css" },
|
||||
{ from: "res/css/views/messages/_EventTileBubble.pcss", type: "css" },
|
||||
{ from: "res/css/views/messages/_MessageActionBar.pcss", type: "css" },
|
||||
{ from: "res/css/views/voip/LegacyCallView/_LegacyCallViewButtons.pcss", type: "css" },
|
||||
{ from: "res/css/views/elements/_ToggleSwitch.pcss", type: "css" },
|
||||
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
declare module "postcss-easings" {
|
||||
export default function (): void;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
declare module "postcss-hexrgba" {
|
||||
export default function (): void;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
declare module "postcss-mixins" {
|
||||
export default function (): void;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
declare module "webpack-version-file-plugin" {
|
||||
interface Opts {
|
||||
outputFile: string;
|
||||
template?: string;
|
||||
templateString?: string;
|
||||
extras?: Record<string, string>;
|
||||
}
|
||||
|
||||
export default class VersionFilePlugin {
|
||||
public constructor(opts: Opts);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,65 @@
|
||||
Changes in [1.12.11](https://github.com/element-hq/element-web/releases/tag/v1.12.11) (2026-02-24)
|
||||
==================================================================================================
|
||||
## 🦖 Deprecations
|
||||
|
||||
* Remove UIFeature.BulkUnverifiedSessionsReminder setting ([#31943](https://github.com/element-hq/element-web/pull/31943)). Contributed by @andybalaam.
|
||||
* Remove unused function to auto-rageshake when key backup is not set up ([#31942](https://github.com/element-hq/element-web/pull/31942)). Contributed by @andybalaam.
|
||||
|
||||
## ✨ Features
|
||||
|
||||
* Room list: update the visuals in order to have better contrast ([#32421](https://github.com/element-hq/element-web/pull/32421)). Contributed by @florianduros.
|
||||
* Set history visibility to "invited" for DMs and new non-public rooms when creating a room ([#31974](https://github.com/element-hq/element-web/pull/31974)). Contributed by @langleyd.
|
||||
* Remove server acl status/summaries from timeline ([#32461](https://github.com/element-hq/element-web/pull/32461)). Contributed by @langleyd.
|
||||
* Update `globalBlacklistUnverifiedDevices` on setting change ([#31983](https://github.com/element-hq/element-web/pull/31983)). Contributed by @kaylendog.
|
||||
* Add badge for history visibiltity to room info panel ([#31927](https://github.com/element-hq/element-web/pull/31927)). Contributed by @richvdh.
|
||||
|
||||
## 🐛 Bug Fixes
|
||||
|
||||
* Default useOnlyCurrentProfiles to true ([#32524](https://github.com/element-hq/element-web/pull/32524)). Contributed by @dbkr.
|
||||
* Keep custom theme active after reload ([#32506](https://github.com/element-hq/element-web/pull/32506)). Contributed by @florianduros.
|
||||
* Update font format from 'ttf' to 'truetype' ([#32493](https://github.com/element-hq/element-web/pull/32493)). Contributed by @all-yall.
|
||||
* Fix videos on Firefox ([#32497](https://github.com/element-hq/element-web/pull/32497)). Contributed by @p1gp1g.
|
||||
* Use a dedicated FAQ/help entry for key storage. ([#32480](https://github.com/element-hq/element-web/pull/32480)). Contributed by @mxandreas.
|
||||
* Avoid showing two chat timelines side by side after a call ([#32484](https://github.com/element-hq/element-web/pull/32484)). Contributed by @robintown.
|
||||
* Update screenshot for reactive display name disambiguation ([#32431](https://github.com/element-hq/element-web/pull/32431)). Contributed by @aditya-cherukuru.
|
||||
* Fix Status Bar being unreadable when the user overrides the default OS light/dark theme. ([#32442](https://github.com/element-hq/element-web/pull/32442)). Contributed by @Half-Shot.
|
||||
* fix: Remove state\_key: null from Seshat search results ([#31524](https://github.com/element-hq/element-web/pull/31524)). Contributed by @shinaoka.
|
||||
* Fix user pill deserialisation ([#31947](https://github.com/element-hq/element-web/pull/31947)). Contributed by @t3chguy.
|
||||
|
||||
|
||||
Changes in [1.12.10](https://github.com/element-hq/element-web/releases/tag/v1.12.10) (2026-02-10)
|
||||
==================================================================================================
|
||||
## ✨ Features
|
||||
|
||||
* Support additional\_creators in /upgraderoom (MSC4289) ([#31934](https://github.com/element-hq/element-web/pull/31934)). Contributed by @andybalaam.
|
||||
* Update room header icon for world\_readable rooms ([#31915](https://github.com/element-hq/element-web/pull/31915)). Contributed by @richvdh.
|
||||
* Show an icon in the room header for shared history ([#31879](https://github.com/element-hq/element-web/pull/31879)). Contributed by @richvdh.
|
||||
* Remove "history may be shared" banner. ([#31881](https://github.com/element-hq/element-web/pull/31881)). Contributed by @kaylendog.
|
||||
* Allow dismissing 'Key storage out of sync' temporarily ([#31455](https://github.com/element-hq/element-web/pull/31455)). Contributed by @andybalaam.
|
||||
* Add `resolutions` entry for `matrix-widget-api` to package.json ([#31851](https://github.com/element-hq/element-web/pull/31851)). Contributed by @toger5.
|
||||
* Improve visibility under contrast control mode ([#31847](https://github.com/element-hq/element-web/pull/31847)). Contributed by @t3chguy.
|
||||
* Unread Sorting - Add option for sorting in `OptionsMenuView` ([#31754](https://github.com/element-hq/element-web/pull/31754)). Contributed by @MidhunSureshR.
|
||||
* Unread sorting - Implement sorter and use it in the room list store ([#31723](https://github.com/element-hq/element-web/pull/31723)). Contributed by @MidhunSureshR.
|
||||
* Allow Element Call widgets to receive sticky events ([#31843](https://github.com/element-hq/element-web/pull/31843)). Contributed by @robintown.
|
||||
* Improve icon rendering accessibility ([#31791](https://github.com/element-hq/element-web/pull/31791)). Contributed by @t3chguy.
|
||||
* Add message preview toggle to room list header option ([#31821](https://github.com/element-hq/element-web/pull/31821)). Contributed by @florianduros.
|
||||
|
||||
## 🐛 Bug Fixes
|
||||
|
||||
* [Backport staging] Fix room list not being cleared ([#32438](https://github.com/element-hq/element-web/pull/32438)). Contributed by @RiotRobot.
|
||||
* Fix failure to update room info panel on joinrule change ([#31938](https://github.com/element-hq/element-web/pull/31938)). Contributed by @richvdh.
|
||||
* Throttle space notification state calculation ([#31922](https://github.com/element-hq/element-web/pull/31922)). Contributed by @dbkr.
|
||||
* Fix emoji verification responsive layout ([#31899](https://github.com/element-hq/element-web/pull/31899)). Contributed by @t3chguy.
|
||||
* Add patch for linkify to fix doctype handling ([#31900](https://github.com/element-hq/element-web/pull/31900)). Contributed by @dbkr.
|
||||
* Fix rooms with no messages appearing at the top of the room list ([#31798](https://github.com/element-hq/element-web/pull/31798)). Contributed by @MidhunSureshR.
|
||||
* Fix room list menu flashes when menu is closed ([#31868](https://github.com/element-hq/element-web/pull/31868)). Contributed by @florianduros.
|
||||
* Message preview toggle is inverted in room list header ([#31865](https://github.com/element-hq/element-web/pull/31865)). Contributed by @florianduros.
|
||||
* Fix duplicate toasts appearing for the same call if two events appear. ([#31693](https://github.com/element-hq/element-web/pull/31693)). Contributed by @Half-Shot.
|
||||
* Fix ability to send rageshake during session restore failure ([#31848](https://github.com/element-hq/element-web/pull/31848)). Contributed by @t3chguy.
|
||||
* Fix mis-alignment of `Threads` right panel title ([#31849](https://github.com/element-hq/element-web/pull/31849)). Contributed by @t3chguy.
|
||||
* Unset buttons does not include color inherit ([#31801](https://github.com/element-hq/element-web/pull/31801)). Contributed by @Philldomd.
|
||||
|
||||
|
||||
Changes in [1.12.9](https://github.com/element-hq/element-web/releases/tag/v1.12.9) (2026-01-27)
|
||||
================================================================================================
|
||||
## ✨ Features
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# syntax=docker.io/docker/dockerfile:1.20-labs@sha256:dbcde2ebc4abc8bb5c3c499b9c9a6876842bf5da243951cd2697f921a7aeb6a9
|
||||
# syntax=docker.io/docker/dockerfile:1.21-labs@sha256:2e681d22e86e738a057075f930b81b2ab8bc2a34cd16001484a7453cfa7a03fb
|
||||
|
||||
# Builder
|
||||
FROM --platform=$BUILDPLATFORM node:24-bullseye@sha256:32bde4fc7635942cafb9681e5479a0ba4b2d53b279e44a67ba9303a71fecd706 AS builder
|
||||
FROM --platform=$BUILDPLATFORM node:24-bullseye@sha256:8036dbe5b1f465e3acb8b866031cd06e4f84c31b0e83dabbdc59397a40dbe288 AS builder
|
||||
|
||||
# Support custom branch of the js-sdk. This also helps us build images of element-web develop.
|
||||
ARG USE_CUSTOM_SDKS=false
|
||||
@@ -11,15 +11,16 @@ ARG JS_SDK_BRANCH="master"
|
||||
WORKDIR /src
|
||||
|
||||
COPY --exclude=docker . /src
|
||||
RUN corepack enable
|
||||
RUN /src/scripts/docker-link-repos.sh
|
||||
RUN yarn --network-timeout=200000 install
|
||||
RUN pnpm install
|
||||
RUN /src/scripts/docker-package.sh
|
||||
|
||||
# Copy the config now so that we don't create another layer in the app image
|
||||
RUN cp /src/config.sample.json /src/webapp/config.json
|
||||
|
||||
# App
|
||||
FROM nginxinc/nginx-unprivileged:alpine-slim@sha256:2c49851f9b34ef35567dc3cbbb56d06d1f56dbb764e75eeb4a599223ee64819c
|
||||
FROM nginxinc/nginx-unprivileged:alpine-slim@sha256:9ac6a908ed07ba7d23cbf6048090453a081abf663c53a7c3f3bf96abc16c0799
|
||||
|
||||
# Need root user to install packages & manipulate the usr directory
|
||||
USER root
|
||||
|
||||
@@ -116,14 +116,14 @@ instead of adding that last line.
|
||||
Element is a modular webapp built with modern ES6 and uses a Node.js build system.
|
||||
Ensure you have the latest LTS version of Node.js installed.
|
||||
|
||||
Using `yarn` instead of `npm` is recommended. Please see the Yarn [install
|
||||
guide](https://classic.yarnpkg.com/en/docs/install) if you do not have it already.
|
||||
Using `pnpm` instead of `npm` is recommended. Please see the pnpm [install
|
||||
guide](https://pnpm.io/installation#using-corepack) if you do not have it already.
|
||||
|
||||
1. Install or update `node.js` so that your `node` is at least the current recommended LTS.
|
||||
1. Install `yarn` if not present already.
|
||||
1. Install `pnpm` if not present already.
|
||||
1. Clone the repo: `git clone https://github.com/element-hq/element-web.git`.
|
||||
1. Switch to the element-web directory: `cd element-web`.
|
||||
1. Install the prerequisites: `yarn install`.
|
||||
1. Install the prerequisites: `pnpm install`.
|
||||
- If you're using the `develop` branch, then it is recommended to set up a
|
||||
proper development environment (see [Setting up a dev
|
||||
environment](./developer_guide.md#setting-up-a-dev-environment) below). Alternatively, you
|
||||
@@ -131,11 +131,11 @@ guide](https://classic.yarnpkg.com/en/docs/install) if you do not have it alread
|
||||
the develop branch.
|
||||
1. Configure the app by copying `config.sample.json` to `config.json` and
|
||||
modifying it. See the [configuration docs](docs/config.md) for details.
|
||||
1. `yarn dist` to build a tarball to deploy. Untaring this file will give
|
||||
1. `pnpm dist` to build a tarball to deploy. Untaring this file will give
|
||||
a version-specific directory containing all the files that need to go on your
|
||||
web server.
|
||||
|
||||
Note that `yarn dist` is not supported on Windows, so Windows users can run `yarn build`,
|
||||
Note that `pnpm dist` is not supported on Windows, so Windows users can run `pnpm build`,
|
||||
which will build all the necessary files into the `webapp` directory. The version of Element
|
||||
will not appear in Settings without using the dist script. You can then mount the
|
||||
`webapp` directory on your web server to actually serve up the app, which is
|
||||
@@ -225,3 +225,6 @@ This software is multi licensed by New Vector Ltd (Element). It can be used eith
|
||||
|
||||
(3) under the terms of a paid-for Element Commercial License agreement between you and Element (the terms of which may vary depending on what you and Element have agreed to).
|
||||
Unless required by applicable law or agreed to in writing, software distributed under the Licenses is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the Licenses for the specific language governing permissions and limitations under the Licenses.
|
||||
|
||||
Please contact [licensing@element.io](mailto:licensing@element.io) to purchase
|
||||
an Element commercial license for this software.
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
# The modules to install. See ./docs/modules.md for more information on
|
||||
# what modules are.
|
||||
#
|
||||
# The values of this are provided to `yarn add` for inclusion.
|
||||
# The values of this are provided to `pnpm add` for inclusion.
|
||||
modules:
|
||||
# An example of pulling a module from NPM
|
||||
- "@vector-im/element-web-ilag-module@^0.0.1"
|
||||
|
||||
@@ -274,20 +274,42 @@ Inheriting all the rules of TypeScript, the following additionally apply:
|
||||
20. Do not use `React.Component::forceUpdate`.
|
||||
21. Prefer to use [compound typography components](https://compound.element.io/?path=/docs/compound-web_typography--docs) instead of raw HTML elements for text. This ensures consistent font usage and letter spacing across the app.
|
||||
22. If you can't use 21, don't forget to apply the correct CSS classes for font and letter spacing.
|
||||
23. Prefer to use `Flex` or `Box` components from shared-components for layout instead of raw HTML elements with CSS flexbox styles.
|
||||
|
||||
## Stylesheets (\*.pcss = PostCSS + Plugins)
|
||||
## Stylesheets
|
||||
|
||||
Note: We use PostCSS + some plugins to process our styles. It looks like SCSS, but actually it is not.
|
||||
1. Keep indentation/nesting to a minimum. Maximum suggested nesting is 5 layers.
|
||||
2. Components should render only within the bounding box of their outermost DOM element. Page-absolute positioning and negative CSS margins and similar are generally not cool and stop the component from being reused easily in different places.
|
||||
|
||||
1. The view's CSS file MUST have the same name as the component (e.g. `view/rooms/_MessageTile.css` for `MessageTile.tsx` component).
|
||||
### PostCSS (\*.pcss = PostCSS + Plugins)
|
||||
|
||||
> [!NOTE]
|
||||
> We use PostCSS + some plugins to process our styles. It looks like SCSS, but actually it is not.
|
||||
|
||||
**PostCSS should be use when working in the main Element Web codebase (not shared-components).**
|
||||
|
||||
#### Naming and file structure
|
||||
|
||||
1. The view's CSS file MUST have the same name as the component (e.g. `res/css/components/views/rooms/_RoomTile.pcss` for `RoomTile.tsx` component).
|
||||
2. Per-view CSS is optional - it could choose to inherit all its styling from the context of the rest of the app, although this is unusual.
|
||||
3. Class names must be prefixed with "mx\_".
|
||||
3. Class names must be prefixed with `mx_`.
|
||||
4. Class names must strictly denote the component which defines them.
|
||||
For example: `mx_MyFoo` for `MyFoo` component.
|
||||
5. Class names for DOM elements within a view which aren't components are named by appending a lower camel case identifier to the view's class name - e.g. .mx_MyFoo_randomDiv is how you'd name the class of an arbitrary div within the MyFoo view.
|
||||
6. Use the `$font` variables instead of manual values.
|
||||
7. Keep indentation/nesting to a minimum. Maximum suggested nesting is 5 layers.
|
||||
8. Use the whole class name instead of shortcuts:
|
||||
5. Class names for DOM elements within a view which aren't components are named by appending a lower camel case identifier to the view's class name.
|
||||
For example: `.mx_MyFoo_randomDiv` is how you'd name the class of an arbitrary div within the MyFoo view.
|
||||
|
||||
#### Variables
|
||||
|
||||
6. Use the `$font-*` variables instead of manual font-size values (e.g., `$font-12px`, `$font-15px`).
|
||||
- Note: These are deprecated. Prefer Compound typography tokens like `var(--cpd-font-body-md-regular)` for new code.
|
||||
7. Use theme color variables like `$primary-content`, `$secondary-content`, `$accent`, `$alert` for colors.
|
||||
- Prefer Compound color tokens like `var(--cpd-color-text-primary)` for new code.
|
||||
8. Use spacing variables like `$spacing-8`, `$spacing-12`, `$spacing-16` where available.
|
||||
- Prefer Compound spacing tokens like `var(--cpd-space-2x)` for new code.
|
||||
|
||||
#### Syntax and formatting
|
||||
|
||||
10. Use the whole class name instead of shortcuts:
|
||||
|
||||
```scss
|
||||
.mx_MyFoo {
|
||||
@@ -298,7 +320,7 @@ Note: We use PostCSS + some plugins to process our styles. It looks like SCSS, b
|
||||
}
|
||||
```
|
||||
|
||||
9. Break multiple selectors over multiple lines this way:
|
||||
11. Break multiple selectors over multiple lines this way:
|
||||
|
||||
```scss
|
||||
.mx_MyFoo,
|
||||
@@ -308,8 +330,7 @@ Note: We use PostCSS + some plugins to process our styles. It looks like SCSS, b
|
||||
}
|
||||
```
|
||||
|
||||
10. Non-shared variables should use $lowerCamelCase. Shared variables use $dashed-naming.
|
||||
11. Overrides to Z indexes, adjustments of dimensions/padding with pixels, and so on should all be
|
||||
12. Overrides to Z indexes, adjustments of dimensions/padding with pixels, and so on should all be
|
||||
[documented](#comments) for what the values mean:
|
||||
|
||||
```scss
|
||||
@@ -320,9 +341,81 @@ Note: We use PostCSS + some plugins to process our styles. It looks like SCSS, b
|
||||
}
|
||||
```
|
||||
|
||||
12. Avoid the use of `!important`. If `!important` is necessary, add a [comment](#comments) explaining why.
|
||||
13. The CSS for a component can override the rules for child components. For instance, .mxRoomList .mx_RoomTile {} would be the selector to override styles of RoomTiles when viewed in the context of a RoomList view. Overrides must be scoped to the View's CSS class - i.e. don't just define .mx_RoomTile {} in RoomList.css - only RoomTile.css is allowed to define its own CSS. Instead, say .mx_RoomList .mx_RoomTile {} to scope the override only to the context of RoomList views. N.B. overrides should be relatively rare as in general CSS inheritance should be enough.
|
||||
14. Components should render only within the bounding box of their outermost DOM element. Page-absolute positioning and negative CSS margins and similar are generally not cool and stop the component from being reused easily in different places.
|
||||
13. Avoid the use of `!important`. If `!important` is necessary, add a [comment](#comments) explaining why.
|
||||
|
||||
#### Component overrides
|
||||
|
||||
14. The CSS for a component can override the rules for child components. For instance, `.mx_RoomList .mx_RoomTile {}` would be the selector to override styles of RoomTiles when viewed in the context of a RoomList view. Overrides must be scoped to the View's CSS class - i.e. don't just define `.mx_RoomTile {}` in RoomList.pcss - only RoomTile.pcss is allowed to define its own CSS. Instead, say `.mx_RoomList .mx_RoomTile {}` to scope the override only to the context of RoomList views. N.B. overrides should be relatively rare as in general CSS inheritance should be enough.
|
||||
|
||||
### CSS module (\*.module.css)
|
||||
|
||||
**CSS modules provide locally-scoped class names and are the preferred approach for new shared components.**
|
||||
|
||||
#### Naming and file structure
|
||||
|
||||
1. The CSS module file MUST have the same name as the component with `.module.css` extension.
|
||||
For example: `PlayPauseButton.module.css` for `PlayPauseButton.tsx`.
|
||||
2. Place the CSS module file in the same directory as the component.
|
||||
3. Class names should be semantic and describe their purpose, NOT prefixed with `mx_`.
|
||||
For example: `.button`, `.label`, `.content`, `.title`.
|
||||
4. Use camelCase for multi-word class names: `.playButton`, `.primaryAction`, `.errorMessage`.
|
||||
|
||||
#### Importing and usage
|
||||
|
||||
5. Import the CSS module as `styles`:
|
||||
|
||||
```tsx
|
||||
import styles from "./MyComponent.module.css";
|
||||
```
|
||||
|
||||
6. Apply classes using the styles object:
|
||||
|
||||
```tsx
|
||||
<div className={styles.container}>
|
||||
<span className={styles.label}>{label}</span>
|
||||
<button className={styles.button}>Click me</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
7. Combine multiple classes using `classNames` utility:
|
||||
|
||||
```tsx
|
||||
import classNames from "classnames";
|
||||
|
||||
<div className={classNames(styles.pill, className)}>
|
||||
<span className={styles.label}>{label}</span>
|
||||
</div>;
|
||||
```
|
||||
|
||||
#### Styling guidelines
|
||||
|
||||
8. Use Compound Design Tokens for all styling values:
|
||||
- Colors: `var(--cpd-color-bg-subtle-primary)`, `var(--cpd-color-text-primary)`
|
||||
- Typography: `var(--cpd-font-body-md-regular)`, `var(--cpd-font-heading-sm-semibold)`
|
||||
- Spacing: `var(--cpd-space-2x)`, `var(--cpd-space-4x)`
|
||||
- Border radius: `var(--cpd-radius-pill-effect)` or standard px values for design-specific needs
|
||||
|
||||
9. Use CSS custom properties for component-specific themeable values:
|
||||
|
||||
```css
|
||||
.flex {
|
||||
display: var(--mx-flex-display, unset);
|
||||
flex-direction: var(--mx-flex-direction, unset);
|
||||
align-items: var(--mx-flex-align, unset);
|
||||
gap: var(--mx-flex-gap, unset);
|
||||
}
|
||||
```
|
||||
|
||||
10. Avoid nesting selectors when possible. CSS modules provide scoping, so nesting is rarely needed.
|
||||
11. If `!important` is necessary (e.g., to override Compound component styles), add a [comment](#comments) explaining why:
|
||||
|
||||
```css
|
||||
.button {
|
||||
background-color: var(--cpd-color-bg-subtle-primary) !important; /* override Compound default */
|
||||
}
|
||||
```
|
||||
|
||||
12. CSS modules do not support PostCSS variables (`$variable`). Always use CSS custom properties (`var(--variable)`) or direct values.
|
||||
|
||||
## Tests
|
||||
|
||||
|
||||
@@ -28,8 +28,7 @@ First clone and build `matrix-js-sdk`:
|
||||
```bash
|
||||
git clone https://github.com/matrix-org/matrix-js-sdk.git
|
||||
pushd matrix-js-sdk
|
||||
yarn link
|
||||
yarn install
|
||||
pnpm install
|
||||
popd
|
||||
```
|
||||
|
||||
@@ -43,12 +42,17 @@ cd element-web
|
||||
Configure the app by copying `config.sample.json` to `config.json` and
|
||||
modifying it. See the [configuration docs](docs/config.md) for details.
|
||||
|
||||
Set up your local development link by creating a `.link-config` file with contents like:
|
||||
|
||||
```
|
||||
matrix-js-sdk=/path/to/matrix-js-sdk
|
||||
```
|
||||
|
||||
Finally, build and start Element itself:
|
||||
|
||||
```bash
|
||||
yarn link matrix-js-sdk
|
||||
yarn install
|
||||
yarn start
|
||||
pnpm install
|
||||
pnpm start
|
||||
```
|
||||
|
||||
Wait a few seconds for the initial build to finish; you should see something like:
|
||||
@@ -104,7 +108,7 @@ There are a number of application-level tests in the `tests` directory; these
|
||||
are designed to run with Jest and JSDOM. To run them
|
||||
|
||||
```
|
||||
yarn test
|
||||
pnpm test
|
||||
```
|
||||
|
||||
### End-to-End tests
|
||||
|
||||
@@ -27,66 +27,119 @@ This is anywhere your data or business logic comes from. If your view model is a
|
||||
1. Located in [`shared-components`](https://github.com/element-hq/element-web/tree/develop/packages/shared-components). Develop it in storybook!
|
||||
2. Views are simple react components (eg: `FooView`) with very little state and logic.
|
||||
3. Views must call `useViewModel` hook with the corresponding view model passed in as argument. This allows the view to re-render when something has changed in the view model. This entire mechanism is powered by [useSyncExternalStore](https://react.dev/reference/react/useSyncExternalStore).
|
||||
4. Views should define the interface of the view model they expect:
|
||||
|
||||
```tsx
|
||||
// Snapshot is the data that your view-model provides which is rendered by the view.
|
||||
interface FooViewSnapshot {
|
||||
value: string;
|
||||
}
|
||||
|
||||
// To call function on the view model
|
||||
interface FooViewActions {
|
||||
doSomething: () => void;
|
||||
}
|
||||
|
||||
// ViewModel is an object (usually a class) that implements both the interfaces listed above.
|
||||
// https://github.com/element-hq/element-web/blob/develop/packages/shared-components/src/ViewModel.ts
|
||||
type FooViewModel = ViewModel<FooViewSnapshot> & FooViewActions;
|
||||
|
||||
interface FooViewProps {
|
||||
// Ideally the view only depends on the view model i.e you don't expect any other props here.
|
||||
vm: FooViewModel;
|
||||
}
|
||||
|
||||
function FooView({ vm }: FooViewProps) {
|
||||
const { value } = useViewModel(vm);
|
||||
return (
|
||||
<button type="button" onClick={() => vm.doSomething()}>
|
||||
{value}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
4. Views should define the interface of the view model (see example below).
|
||||
5. Multiple views can share the same view model if necessary.
|
||||
6. A full example is available [here](https://github.com/element-hq/element-web/blob/develop/packages/shared-components/src/audio/AudioPlayerView/AudioPlayerView.tsx)
|
||||
|
||||
**Example of view implementation**
|
||||
|
||||
```tsx
|
||||
// Snapshot is the data that your view-model provides which is rendered by the view.
|
||||
export interface FooViewSnapshot {
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
// To call function on the view model
|
||||
interface FooViewActions {
|
||||
setTitle: (title: string) => void;
|
||||
reloadDescription: () => void;
|
||||
}
|
||||
|
||||
// ViewModel is an object (usually a class) that implements both the interfaces listed above.
|
||||
// https://github.com/element-hq/element-web/blob/develop/packages/shared-components/src/ViewModel.ts
|
||||
export type FooViewModel = ViewModel<FooViewSnapshot> & FooViewActions;
|
||||
|
||||
interface FooViewProps {
|
||||
// Ideally the view only depends on the view model i.e you don't expect any other props here.
|
||||
vm: FooViewModel;
|
||||
}
|
||||
|
||||
export function FooView({ vm }: FooViewProps): JSX.Element {
|
||||
// useViewModel is a hook that subscribes to the view model and returns the snapshot. It also ensures that the component re-renders when the snapshot changes.
|
||||
const { title, description } = useViewModel(vm);
|
||||
return (
|
||||
<section>
|
||||
<h1>{title}</h1>
|
||||
{/* Bind setTitle action */}
|
||||
<button type="button" onClick={() => vm.setTitle("new title!")}>
|
||||
Set title
|
||||
</button>
|
||||
<p>{description}</p>
|
||||
{/* Bind reloadDescription action */}
|
||||
<button type="button" onClick={() => vm.reloadDescription()}>
|
||||
Reload description
|
||||
</button>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
#### View Model
|
||||
|
||||
1. A View model is a class extending [`BaseViewModel`](https://github.com/element-hq/element-web/blob/develop/src/viewmodels/base/BaseViewModel.ts).
|
||||
2. Implements the interface defined in the view (e.g `FooViewModel` in the example above).
|
||||
3. View models define a snapshot type that defines the data the view will consume. The snapshot is immutable and can only be changed by calling `this.snapshot.set(...)` in the view model. This will trigger a re-render in the view.
|
||||
3. View models define a snapshot type that defines the data the view will consume. The snapshot is immutable and can only be changed by calling `this.snapshot.set(...)` or `this.snapshot.merge(...)` in the view model. This will trigger a re-render in the view.
|
||||
4. Call [`this.snapshot.merge(...)`](https://github.com/element-hq/element-web/blob/develop/packages/shared-components/src/viewmodel/Snapshot.ts#L32) to only update part of the snapshot.
|
||||
5. Avoid recomputing the entire snapshot when you only need to update a single field. For performance reasons, only recompute the fields that have actually changed. For example, if only `title` has changed, call `this.snapshot.merge({ title: newTitle })` rather than rebuilding the full snapshot object with all fields recomputed.
|
||||
6. View models can have props which are passed in the constructor. Props are usually used to pass in dependencies (eg: stores, sdk, etc) that the view model needs to do its work. They can also be used to pass in initial values for the snapshot.
|
||||
|
||||
```ts
|
||||
interface Props {
|
||||
propsValue: string;
|
||||
**Example of a view model implementation**
|
||||
|
||||
```ts
|
||||
import { type FooViewSnapshot, type FooViewModel as FooViewModelInterface } from "./FooView";
|
||||
|
||||
// Props are the arguments passed to the view model constructor. They are usually used to pass in dependencies (eg: stores, sdk, etc) that the view model needs to do its work. They can also be used to pass in initial values for the snapshot.
|
||||
interface Props {
|
||||
title: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is an example view model that implements the FooViewModelInterface.
|
||||
* It extends the BaseViewModel class which provides common functionality for view models, such as managing subscriptions and snapshots.
|
||||
* The view model is responsible for managing the state of the view and providing actions that can be called from the view.
|
||||
* In this example, we have a title and description in the snapshot, and actions to set the title and reload the description.
|
||||
*/
|
||||
export class FooViewModel extends BaseViewModel<FooViewSnapshot, Props> implements FooViewModelInterface {
|
||||
public constructor(props: Props) {
|
||||
// Call super with initial snapshot
|
||||
super(props, { title: props.title, description: costlyDescriptionLoading() });
|
||||
}
|
||||
|
||||
class FooViewModel extends BaseViewModel<FooViewSnapshot, Props> implements FooViewModel {
|
||||
constructor(props: Props) {
|
||||
// Call super with initial snapshot
|
||||
super(props, { value: "initial" });
|
||||
}
|
||||
|
||||
public doSomething() {
|
||||
// Call this.snapshot.set to update the snapshot
|
||||
this.snapshot.set({ value: "changed" });
|
||||
}
|
||||
public setTitle(title: string): void {
|
||||
// We only update the title in the snapshot, description remains unchanged.
|
||||
// Calling `this.snapshot.merge` will trigger the view to re-render with the new snapshot value.
|
||||
// If we had called `this.snapshot.set`, we would have needed to provide the full snapshot value, including the description.
|
||||
this.snapshot.merge({ title });
|
||||
}
|
||||
```
|
||||
|
||||
4. A full example is available [here](https://github.com/element-hq/element-web/blob/develop/src/viewmodels/audio/AudioPlayerViewModel.ts)
|
||||
public reloadDescription(): void {
|
||||
// Simulate reloading the description by calling the costly function again and updating the snapshot.
|
||||
this.snapshot.merge({ description: costlyDescriptionLoading() });
|
||||
}
|
||||
|
||||
/**
|
||||
* This is an example of how to access props in the view model. Props are passed in the constructor and can be accessed through `this.props`.
|
||||
*/
|
||||
public printProps(): void {
|
||||
// Access props through `this.props`
|
||||
console.log("Current props:", this.props);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Binding of View Model Actions:
|
||||
|
||||
All view model actions must be defined as arrow functions to ensure they are bound to the class instance.
|
||||
|
||||
Using standard class methods can result in `this` being undefined when the function is passed as a callback (e.g. to a React event handler), which may cause runtime errors.
|
||||
|
||||
Correct pattern:
|
||||
|
||||
```ts
|
||||
public doSomething = (): void => {
|
||||
...
|
||||
};
|
||||
```
|
||||
|
||||
### `useViewModel` hook
|
||||
|
||||
|
||||
@@ -160,7 +160,8 @@ complete re-branding/private labeling, a more personalised experience can be ach
|
||||
3. `show_once`: Optional. If true then the notice will only be shown once per device.
|
||||
19. `help_url`: The URL to point users to for help with the app, defaults to `https://element.io/help`.
|
||||
20. `help_encryption_url`: The URL to point users to for help with encryption, defaults to `https://element.io/help#encryption`.
|
||||
21. `force_verification`: If true, users must verify new logins (eg. with another device / their recovery key)
|
||||
21. `help_key_storage_url`: The URL to point users to for help with key storage, defaults to `https://element.io/help#encryption5`.
|
||||
22. `force_verification`: If true, users must verify new logins (eg. with another device / their recovery key)
|
||||
|
||||
### `desktop_builds` and `mobile_builds`
|
||||
|
||||
@@ -249,7 +250,7 @@ Together, the options might look like this in your config:
|
||||
Note that `index.html` also has an og:image meta tag that is set to an image hosted on element.io. This is the image used if
|
||||
links to your copy of Element appear in some websites like Facebook, and indeed Element itself. This has to be static in the HTML
|
||||
and an absolute URL (and HTTP rather than HTTPS), so it's not possible for this to be an option in config.json. If you'd like to
|
||||
change it, you can build Element, but run `RIOT_OG_IMAGE_URL="http://example.com/logo.png" yarn build`. Alternatively, you can edit
|
||||
change it, you can build Element, but run `RIOT_OG_IMAGE_URL="http://example.com/logo.png" pnpm build`. Alternatively, you can edit
|
||||
the `og:image` meta tag in `index.html` directly each time you download a new version of Element.
|
||||
|
||||
## SSO setup
|
||||
@@ -581,8 +582,6 @@ Currently, the following UI feature flags are supported:
|
||||
This should only be used if the room history visibility options are managed by the server.
|
||||
- `UIFeature.TimelineEnableRelativeDates` - Display relative date separators (eg: 'Today', 'Yesterday') in the
|
||||
timeline for recent messages. When false day dates will be used.
|
||||
- `UIFeature.BulkUnverifiedSessionsReminder` - Display popup reminders to verify or remove unverified sessions. Defaults
|
||||
to true.
|
||||
- `UIFeature.locationSharing` - Whether or not location sharing menus will be shown.
|
||||
- `UIFeature.allowCreatingPublicRooms` - Whether or not public rooms can be created.
|
||||
- `UIFeature.allowCreatingPublicSpaces` - Whether or not public spaces can be created.
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
> DEPRECATED. Use [Element web module api](https://github.com/element-hq/element-modules/tree/main/packages/element-web-module-api) instead.
|
||||
|
||||
The module system in Element Web is a way to add or modify functionality of Element Web itself, bundled at compile time
|
||||
for the app. This means that modules are loaded as part of the `yarn build` process but have an effect on user experience
|
||||
for the app. This means that modules are loaded as part of the `pnpm build` process but have an effect on user experience
|
||||
at runtime.
|
||||
|
||||
## Installing modules
|
||||
|
||||
If you already have a module you want to install, such as our [ILAG Module](https://github.com/element-hq/element-web-ilag-module),
|
||||
then copy `build_config.sample.yaml` to `build_config.yaml` in the same directory. In your new `build_config.yaml` simply
|
||||
add the reference to the module as described by the sample file, using the same syntax you would for `yarn add`:
|
||||
add the reference to the module as described by the sample file, using the same syntax you would for `pnpm add`:
|
||||
|
||||
```yaml
|
||||
modules:
|
||||
@@ -19,7 +19,7 @@ modules:
|
||||
- "@vector-im/element-web-ilag-module@latest"
|
||||
```
|
||||
|
||||
Then build the app as you normally would: `yarn build` or `yarn dist` (if compatible on your platform). If you are building
|
||||
Then build the app as you normally would: `pnpm build` or `pnpm dist` (if compatible on your platform). If you are building
|
||||
the Docker image then ensure your `build_config.yaml` ends up in the build directory. Usually this works fine if you use
|
||||
the current directory as the build context (the `.` in `docker build -t my-element-web .`).
|
||||
|
||||
@@ -41,9 +41,9 @@ The following requirements are key for any module:
|
||||
1. The module must depend on `@matrix-org/react-sdk-module-api` (usually as a dev dependency).
|
||||
2. The module's `main` entrypoint must have a `default` export for the `RuntimeModule` instance, supporting a constructor
|
||||
which takes a single parameter: a `ModuleApi` instance. This instance is passed to `super()`.
|
||||
3. The module must be deployed in a way where `yarn add` can access it, as that is how the build system will try to
|
||||
3. The module must be deployed in a way where `pnpm add` can access it, as that is how the build system will try to
|
||||
install it. Note that while this is often NPM, it can also be a GitHub/GitLab repo or private NPM registry.
|
||||
Be careful when using git dependencies in yarn classic, many lifecycle scripts will not be executed which may mean
|
||||
Be careful when using git dependencies in pnpm classic, many lifecycle scripts will not be executed which may mean
|
||||
that your module is not built and thus may fail to be imported.
|
||||
|
||||
... and that's pretty much it. As with any code, please be responsible and call things in line with the documentation.
|
||||
|
||||
@@ -43,7 +43,7 @@ Follow the Playwright installation instructions:
|
||||
- **System dependencies:** <https://playwright.dev/docs/browsers#install-system-dependencies>
|
||||
|
||||
```sh
|
||||
yarn playwright install --with-deps
|
||||
pnpm playwright install --with-deps
|
||||
```
|
||||
|
||||
### 2. Container Runtime
|
||||
@@ -56,7 +56,7 @@ Element Web E2E tests require an instance running on `http://localhost:8080` (co
|
||||
|
||||
You can either:
|
||||
|
||||
- **Run manually:** `yarn start` in a separate terminal (not working for screenshot tests running in a docker environment).
|
||||
- **Run manually:** `pnpm start` in a separate terminal (not working for screenshot tests running in a docker environment).
|
||||
- **Auto-start:** Playwright will start the webserver automatically if it's not already running
|
||||
|
||||
## Running the Tests
|
||||
@@ -68,19 +68,19 @@ Our main Playwright tests run against a full Element Web instance with Synapse/D
|
||||
**Run all E2E tests:**
|
||||
|
||||
```sh
|
||||
yarn run test:playwright
|
||||
pnpm run test:playwright
|
||||
```
|
||||
|
||||
**Run a specific test file:**
|
||||
|
||||
```sh
|
||||
yarn run test:playwright playwright/e2e/register/register.spec.ts
|
||||
pnpm run test:playwright playwright/e2e/register/register.spec.ts
|
||||
```
|
||||
|
||||
**Run tests interactively with Playwright UI:**
|
||||
|
||||
```sh
|
||||
yarn run test:playwright:open
|
||||
pnpm run test:playwright:open
|
||||
```
|
||||
|
||||
**Run screenshot tests only:**
|
||||
@@ -89,7 +89,7 @@ yarn run test:playwright:open
|
||||
> This command run the playwright tests in a docker environment.
|
||||
|
||||
```sh
|
||||
yarn run test:playwright:screenshots
|
||||
pnpm run test:playwright:screenshots
|
||||
```
|
||||
|
||||
For more information about visual testing, see [Visual Testing](playwright#visual-testing).
|
||||
@@ -98,30 +98,7 @@ For more information about visual testing, see [Visual Testing](playwright#visua
|
||||
|
||||
### Shared Components Tests
|
||||
|
||||
The shared-components package uses Playwright (via Storybook test runner) to validate component rendering across different states and configurations.
|
||||
|
||||
**Run Storybook tests:**
|
||||
|
||||
```sh
|
||||
cd packages/shared-components
|
||||
yarn test:storybook
|
||||
```
|
||||
|
||||
**Run Storybook tests in CI mode:**
|
||||
|
||||
```sh
|
||||
cd packages/shared-components
|
||||
yarn test:storybook:ci
|
||||
```
|
||||
|
||||
**Update Storybook screenshots:**
|
||||
|
||||
```sh
|
||||
cd packages/shared-components
|
||||
yarn test:storybook:update
|
||||
```
|
||||
|
||||
This uses the same Docker-based screenshot rendering as Element Web to ensure consistency across platforms.
|
||||
See the [Shared Components README](../packages/shared-components/README.md#visual-regression-tests) for instructions on running the shared components Playwright tests.
|
||||
|
||||
### Projects
|
||||
|
||||
@@ -148,13 +125,6 @@ Misc:
|
||||
- `playwright/pages/` - Page object models
|
||||
- `playwright/plugins/` - Custom Playwright plugins
|
||||
|
||||
**Shared components tests** are located in `packages/shared-components/`:
|
||||
|
||||
- `packages/shared-components/playwright/snapshots/` - Storybook screenshot baselines
|
||||
- `packages/shared-components/.storybook/` - Storybook configuration
|
||||
|
||||
The shared components use Storybook's test runner (powered by Playwright) to validate component rendering across different states and configurations.
|
||||
|
||||
### Homeserver Setup
|
||||
|
||||
Homeservers (Synapse or Dendrite) are launched by Playwright workers and reused for all tests matching the worker configuration.
|
||||
@@ -417,7 +387,7 @@ This command runs only tests tagged with `@screenshot` in the Docker environment
|
||||
When you need to update screenshot baselines (e.g., after intentional UI changes):
|
||||
|
||||
```sh
|
||||
yarn run test:playwright:screenshots
|
||||
pnpm run test:playwright:screenshots
|
||||
```
|
||||
|
||||
**Important:** Always use this command to update screenshots rather than running tests locally with `--update-snapshots`.
|
||||
@@ -477,10 +447,3 @@ export TMPDIR=/tmp/colima
|
||||
# or
|
||||
export TMPDIR=$HOME/tmp
|
||||
```
|
||||
|
||||
**macOS users:**
|
||||
|
||||
Docker Desktop and Colima are both well-supported on macOS.
|
||||
|
||||
> [!CAUTION]
|
||||
> Do not set `DOCKER_HOST` when running tests. Element Web uses [element-web-playwright-common](https://github.com/element-hq/element-modules/tree/main/packages/element-web-playwright-common), and setting `DOCKER_HOST` causes issues with testcontainers when running in the container VM.
|
||||
|
||||
@@ -39,7 +39,7 @@ e.g. in config.json:
|
||||
"faces": [
|
||||
{
|
||||
"font-family": "Inter",
|
||||
"src": [{"url": "/fonts/Inter.ttf", "format": "ttf"}]
|
||||
"src": [{"url": "/fonts/Inter.ttf", "format": "truetype"}]
|
||||
}
|
||||
],
|
||||
"general": "Inter, sans",
|
||||
|
||||
@@ -50,7 +50,7 @@ We are aiming for a set of common strings to be shared then some more localised
|
||||
|
||||
1. Check if the import `import { _t } from ".../languageHandler";` is present. If not add it to the other import statements. Also import `_td` if needed.
|
||||
1. Add `_t()` to your string passing the translation key you come up with based on the rules above. If the string is introduced at a point before the translation system has not yet been initialized, use `_td()` instead, and call `_t()` at the appropriate time.
|
||||
1. Run `yarn i18n` to add the keys to `src/i18n/strings/en_EN.json`
|
||||
1. Run `pnpm i18n` to add the keys to `src/i18n/strings/en_EN.json`
|
||||
1. Modify the new entries in `src/i18n/strings/en_EN.json` with the English (UK) translations for the added keys.
|
||||
|
||||
## Editing existing strings
|
||||
|
||||
@@ -40,9 +40,10 @@ const config: Config = {
|
||||
"^!!raw-loader!.*": "jest-raw-loader",
|
||||
"recorderWorkletFactory": "<rootDir>/__mocks__/empty.js",
|
||||
"counterpart": "<rootDir>/node_modules/counterpart",
|
||||
"@vector-im/compound-web": "<rootDir>/node_modules/@vector-im/compound-web",
|
||||
},
|
||||
transformIgnorePatterns: [
|
||||
"/node_modules/(?!(mime|matrix-js-sdk|uuid|p-retry|is-network-error|react-merge-refs|is-ip|ip-regex|super-regex|function-timeout|time-span|convert-hrtime|clone-regexp|is-regexp|matrix-web-i18n)).+$",
|
||||
"/node_modules/(?!(mime|matrix-js-sdk|uuid|p-retry|is-network-error|react-merge-refs|is-ip|ip-regex|super-regex|function-timeout|time-span|convert-hrtime|clone-regexp|is-regexp|matrix-web-i18n|await-lock|@element-hq/web-shared-components|react-virtuoso|lodash)).+$",
|
||||
],
|
||||
collectCoverageFrom: [
|
||||
"<rootDir>/src/**/*.{js,ts,tsx}",
|
||||
|
||||
@@ -1,28 +1,35 @@
|
||||
import { KnipConfig } from "knip";
|
||||
|
||||
export default {
|
||||
entry: [
|
||||
"src/serviceworker/index.ts",
|
||||
"src/workers/*.worker.ts",
|
||||
"src/utils/exportUtils/exportJS.js",
|
||||
"src/vector/localstorage-fix.ts",
|
||||
"scripts/**",
|
||||
"playwright/**",
|
||||
"test/**",
|
||||
"res/decoder-ring/**",
|
||||
"res/jitsi_external_api.min.js",
|
||||
"docs/**",
|
||||
],
|
||||
project: ["**/*.{js,ts,jsx,tsx}"],
|
||||
ignore: [
|
||||
// Keep for now
|
||||
"src/hooks/useLocalStorageState.ts",
|
||||
"src/hooks/useTimeout.ts",
|
||||
"src/components/views/elements/InfoTooltip.tsx",
|
||||
"src/components/views/elements/StyledCheckbox.tsx",
|
||||
workspaces: {
|
||||
"packages/shared-components": {
|
||||
entry: ["src/index.ts"],
|
||||
},
|
||||
".": {
|
||||
entry: [
|
||||
"src/serviceworker/index.ts",
|
||||
"src/workers/*.worker.ts",
|
||||
"src/utils/exportUtils/exportJS.js",
|
||||
"src/vector/localstorage-fix.ts",
|
||||
"scripts/**",
|
||||
"playwright/**",
|
||||
"test/**",
|
||||
"res/decoder-ring/**",
|
||||
"res/jitsi_external_api.min.js",
|
||||
"docs/**",
|
||||
],
|
||||
ignore: [
|
||||
// Keep for now
|
||||
"src/hooks/useLocalStorageState.ts",
|
||||
"src/hooks/useTimeout.ts",
|
||||
"src/components/views/elements/InfoTooltip.tsx",
|
||||
"src/components/views/elements/StyledCheckbox.tsx",
|
||||
|
||||
"packages/**/*",
|
||||
],
|
||||
"packages/**/*",
|
||||
],
|
||||
},
|
||||
},
|
||||
ignoreBinaries: ["test:storybook", "test:unit", "build:storybook"],
|
||||
ignoreDependencies: [
|
||||
// Required for `action-validator`
|
||||
"@action-validator/*",
|
||||
@@ -43,22 +50,20 @@ export default {
|
||||
// Embedded into webapp
|
||||
"@element-hq/element-call-embedded",
|
||||
// Transitive dep of jest
|
||||
"@jest/globals",
|
||||
"vitest-environment-jest-fixed-jsdom",
|
||||
|
||||
// We import this in some tests, transitive dep of @playwright/test
|
||||
"playwright-core",
|
||||
|
||||
// Used by matrix-js-sdk, which means we have to include them as a
|
||||
// dependency so that // we can run `tsc` (since we import the typescript
|
||||
// source of js-sdk, rather than the transpiled and annotated JS like you
|
||||
// would with a normal library).
|
||||
"@types/content-type",
|
||||
"@types/sdp-transform",
|
||||
|
||||
// Used in EW but failed because of "link:"
|
||||
"@element-hq/web-shared-components",
|
||||
],
|
||||
ignoreBinaries: [
|
||||
// Used in scripts & workflows
|
||||
"jq",
|
||||
],
|
||||
ignoreExportsUsedInFile: true,
|
||||
nx: {
|
||||
config: ["nx.json", "project.json", "{apps,packages,modules}/**/project.json", "package.json"],
|
||||
},
|
||||
} satisfies KnipConfig;
|
||||
|
||||
@@ -38,7 +38,7 @@ export function installer(config: BuildConfig): void {
|
||||
let exitCode = 0;
|
||||
|
||||
// We cheat a bit and store the current package.json and lockfile so we can safely
|
||||
// run `yarn add` without creating extra committed files for people. We restore
|
||||
// run `pnpm add` without creating extra committed files for people. We restore
|
||||
// these files by simply overwriting them when we're done.
|
||||
const packageDeps = readCurrentPackageDetails();
|
||||
|
||||
@@ -47,7 +47,7 @@ export function installer(config: BuildConfig): void {
|
||||
const currentOptDeps = getOptionalDepNames(packageDeps.packageJson);
|
||||
|
||||
try {
|
||||
// Install the modules with yarn
|
||||
// Install the modules with pnpm
|
||||
const yarnAddRef = config.modules.join(" ");
|
||||
callYarnAdd(yarnAddRef); // install them all at once
|
||||
|
||||
@@ -108,20 +108,20 @@ type RawDependencies = {
|
||||
|
||||
function readCurrentPackageDetails(): RawDependencies {
|
||||
return {
|
||||
lockfile: fs.readFileSync("./yarn.lock", "utf-8"),
|
||||
lockfile: fs.readFileSync("./pnpm-lock.yaml", "utf-8"),
|
||||
packageJson: fs.readFileSync("./package.json", "utf-8"),
|
||||
};
|
||||
}
|
||||
|
||||
function writePackageDetails(deps: RawDependencies): void {
|
||||
fs.writeFileSync("./yarn.lock", deps.lockfile, "utf-8");
|
||||
fs.writeFileSync("./pnpm-lock.yaml", deps.lockfile, "utf-8");
|
||||
fs.writeFileSync("./package.json", deps.packageJson, "utf-8");
|
||||
}
|
||||
|
||||
function callYarnAdd(dep: string): void {
|
||||
// Add the module to the optional dependencies section just in case something
|
||||
// goes wrong in restoring the original package details.
|
||||
childProcess.execSync(`yarn add -O ${dep}`, {
|
||||
childProcess.execSync(`pnpm add -O ${dep}`, {
|
||||
env: process.env,
|
||||
stdio: ["inherit", "inherit", "inherit"],
|
||||
});
|
||||
@@ -170,7 +170,7 @@ function getTopLevelDependencyVersion(dep: string): string {
|
||||
|
||||
function getModuleApiVersionFor(moduleName: string): string {
|
||||
// We'll just pretend that this isn't highly problematic...
|
||||
// Yarn is fairly stable in putting modules in a flat hierarchy, at least.
|
||||
// pnpm is fairly stable in putting modules in a flat hierarchy, at least.
|
||||
const pkgJsonStr = fs.readFileSync(`./node_modules/${moduleName}/package.json`, "utf-8");
|
||||
return findDepVersionInPackageJson(moduleApiDepName, pkgJsonStr);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"$schema": "./node_modules/nx/schemas/nx-schema.json",
|
||||
"defaultBase": "develop",
|
||||
"targetDefaults": {
|
||||
"build": {
|
||||
"dependsOn": ["^build", "prebuild:*"]
|
||||
}
|
||||
},
|
||||
"namedInputs": {
|
||||
"src": ["{projectRoot}/src/**/*"]
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "element-web",
|
||||
"version": "1.12.9",
|
||||
"version": "1.12.11",
|
||||
"description": "Element: the future of secure communication",
|
||||
"author": "New Vector Ltd.",
|
||||
"repository": {
|
||||
@@ -8,97 +8,68 @@
|
||||
"url": "https://github.com/element-hq/element-web"
|
||||
},
|
||||
"license": "SEE LICENSE IN README.md",
|
||||
"files": [
|
||||
"lib",
|
||||
"res",
|
||||
"src",
|
||||
"webpack.config.js",
|
||||
"scripts",
|
||||
"docs",
|
||||
"release.sh",
|
||||
"deploy",
|
||||
"CHANGELOG.md",
|
||||
"CONTRIBUTING.rst",
|
||||
"LICENSE",
|
||||
"README.md",
|
||||
"AUTHORS.rst",
|
||||
"package.json"
|
||||
],
|
||||
"style": "bundle.css",
|
||||
"type": "module",
|
||||
"matrix_i18n_extra_translation_funcs": [
|
||||
"UserFriendlyError"
|
||||
],
|
||||
"scripts": {
|
||||
"i18n": "matrix-gen-i18n src res && yarn i18n:sort && yarn i18n:lint",
|
||||
"i18n:sort": "matrix-sort-i18n src/i18n/strings/en_EN.json && yarn --cwd packages/shared-components i18n:sort",
|
||||
"i18n:lint": "matrix-i18n-lint && prettier --log-level=silent --write src/i18n/strings/ --ignore-path /dev/null && yarn --cwd packages/shared-components i18n:lint",
|
||||
"i18n:diff": "cp src/i18n/strings/en_EN.json src/i18n/strings/en_EN_orig.json && yarn i18n && matrix-compare-i18n-files src/i18n/strings/en_EN_orig.json src/i18n/strings/en_EN.json",
|
||||
"make-component": "node scripts/make-react-component.js",
|
||||
"rethemendex": "./res/css/rethemendex.sh",
|
||||
"clean": "rimraf lib webapp",
|
||||
"build": "yarn clean && yarn build:genfiles && yarn build:bundle",
|
||||
"build-stats": "yarn clean && yarn build:genfiles && yarn build:bundle-stats",
|
||||
"build:res": "node scripts/copy-res.ts",
|
||||
"build:genfiles": "yarn build:res && yarn build:module_system",
|
||||
"build:modernizr": "modernizr -c .modernizr.json -d src/vector/modernizr.js",
|
||||
"build:bundle": "webpack --progress --mode production",
|
||||
"build:bundle-stats": "webpack --progress --mode production --json > webpack-stats.json",
|
||||
"build:module_system": "node module_system/scripts/install.ts",
|
||||
"i18n": "matrix-gen-i18n src res && pnpm i18n:sort && pnpm i18n:lint",
|
||||
"i18n:sort": "matrix-sort-i18n src/i18n/strings/en_EN.json && pnpm --dir packages/shared-components i18n:sort",
|
||||
"i18n:lint": "matrix-i18n-lint && prettier --log-level=silent --write src/i18n/strings/ --ignore-path /dev/null && pnpm --dir packages/shared-components i18n:lint",
|
||||
"i18n:diff": "cp src/i18n/strings/en_EN.json src/i18n/strings/en_EN_orig.json && pnpm i18n && matrix-compare-i18n-files src/i18n/strings/en_EN_orig.json src/i18n/strings/en_EN.json",
|
||||
"rethemendex": "sh ./res/css/rethemendex.sh",
|
||||
"build": "nx build",
|
||||
"build-stats": "nx build --json=webpack-stats.json",
|
||||
"vendor:modernizr": "modernizr -c .modernizr.json -d src/vector/modernizr.js && mv src/vector/modernizr.js src/vector/modernizr.cjs",
|
||||
"vendor:jitsi": "curl -s https://meet.element.io/libs/external_api.min.js > ./res/jitsi_external_api.min.js",
|
||||
"dist": "./scripts/package.sh",
|
||||
"start": "concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n modules,res \"yarn build:module_system\" \"yarn build:res\" && concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n res,element-js \"yarn start:res\" \"yarn start:js\"",
|
||||
"start:https": "concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n res,element-js \"yarn start:res\" \"yarn start:js --server-type https\"",
|
||||
"start:res": "node scripts/copy-res.ts -w",
|
||||
"start:js": "webpack serve --output-path webapp --output-filename=bundles/_dev_/[name].js --output-chunk-filename=bundles/_dev_/[name].js --mode development",
|
||||
"lint": "yarn lint:types && yarn lint:js && yarn lint:style && yarn lint:workflows",
|
||||
"start": "nx start",
|
||||
"lint": "pnpm lint:types && pnpm lint:js && pnpm lint:style && pnpm lint:workflows",
|
||||
"lint:js": "eslint --max-warnings 0 src test playwright module_system && prettier --check .",
|
||||
"lint:js-fix": "prettier --log-level=warn --write . && eslint --fix src test playwright module_system",
|
||||
"lint:types": "yarn lint:types:src && yarn lint:types:module_system",
|
||||
"lint:types:src": "tsc --noEmit --jsx react && tsc --noEmit --jsx react -p playwright",
|
||||
"lint:types:module_system": "tsc --noEmit --project ./tsconfig.module_system.json",
|
||||
"lint:types": "nx lint:types",
|
||||
"lint:style": "stylelint \"res/css/**/*.pcss\"",
|
||||
"lint:workflows": "find .github/workflows -type f \\( -iname '*.yaml' -o -iname '*.yml' \\) | xargs -I {} sh -c 'echo \"Linting {}\"; action-validator \"{}\"'",
|
||||
"lint:knip": "knip",
|
||||
"test": "jest",
|
||||
"test": "nx test:unit",
|
||||
"test:playwright": "playwright test",
|
||||
"test:playwright:open": "yarn test:playwright --ui",
|
||||
"test:playwright:screenshots": "playwright-screenshots --project=Chrome",
|
||||
"coverage": "yarn test --coverage",
|
||||
"test:playwright:open": "pnpm test:playwright --ui",
|
||||
"test:playwright:screenshots": "playwright-screenshots-experimental pnpm playwright test --update-snapshots --project=Chrome --grep @screenshot",
|
||||
"coverage": "pnpm test --coverage",
|
||||
"analyse:webpack-bundles": "webpack-bundle-analyzer webpack-stats.json webapp",
|
||||
"update:jitsi": "curl -s https://meet.element.io/libs/external_api.min.js > ./res/jitsi_external_api.min.js",
|
||||
"install": "yarn --cwd packages/shared-components install --frozen-lockfile",
|
||||
"postinstall": "patch-package"
|
||||
"postinstall": "node scripts/pnpm-link.ts"
|
||||
},
|
||||
"resolutions": {
|
||||
"**/pretty-format/react-is": "19.2.3",
|
||||
"@types/react": "19.2.7",
|
||||
"@types/react-dom": "19.2.3",
|
||||
"pretty-format@30>react-is": "19.2.4",
|
||||
"@types/react": "catalog:",
|
||||
"@types/react-dom": "catalog:",
|
||||
"oidc-client-ts": "3.4.1",
|
||||
"jwt-decode": "4.0.0",
|
||||
"caniuse-lite": "1.0.30001762",
|
||||
"caniuse-lite": "1.0.30001766",
|
||||
"testcontainers": "^11.0.0",
|
||||
"wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0",
|
||||
"wrap-ansi": "npm:wrap-ansi@^7.0.0"
|
||||
"wrap-ansi": "npm:wrap-ansi@^7.0.0",
|
||||
"matrix-widget-api": "^1.16.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@element-hq/element-web-module-api": "1.9.0",
|
||||
"@element-hq/web-shared-components": "link:packages/shared-components",
|
||||
"@element-hq/element-web-module-api": "catalog:",
|
||||
"@element-hq/web-shared-components": "workspace:*",
|
||||
"@fontsource/fira-code": "^5",
|
||||
"@fontsource/inter": "^5",
|
||||
"@formatjs/intl-segmenter": "^12.0.0",
|
||||
"@matrix-org/analytics-events": "^0.30.0",
|
||||
"@matrix-org/analytics-events": "^0.31.0",
|
||||
"@matrix-org/emojibase-bindings": "^1.5.0",
|
||||
"@matrix-org/react-sdk-module-api": "^2.4.0",
|
||||
"@matrix-org/spec": "^1.7.0",
|
||||
"@sentry/browser": "^10.0.0",
|
||||
"@types/png-chunks-extract": "^1.0.2",
|
||||
"@vector-im/compound-design-tokens": "6.8.0",
|
||||
"@vector-im/compound-web": "^8.3.5",
|
||||
"@vector-im/compound-design-tokens": "catalog:",
|
||||
"@vector-im/compound-web": "catalog:",
|
||||
"@vector-im/matrix-wysiwyg": "2.40.0",
|
||||
"@zxcvbn-ts/core": "^3.0.4",
|
||||
"@zxcvbn-ts/language-common": "^3.0.4",
|
||||
"@zxcvbn-ts/language-en": "^3.0.2",
|
||||
"await-lock": "^2.1.0",
|
||||
"await-lock": "^3.0.0",
|
||||
"bloom-filters": "^3.0.3",
|
||||
"blurhash": "^2.0.3",
|
||||
"browserslist": "^4.23.2",
|
||||
@@ -126,34 +97,31 @@
|
||||
"linkify-react": "4.3.2",
|
||||
"linkify-string": "4.3.2",
|
||||
"linkifyjs": "4.3.2",
|
||||
"lodash": "^4.17.21",
|
||||
"lodash": "npm:lodash-es@^4.17.21",
|
||||
"maplibre-gl": "^5.0.0",
|
||||
"matrix-encrypt-attachment": "^1.0.3",
|
||||
"matrix-js-sdk": "40.1.0",
|
||||
"matrix-widget-api": "^1.15.0",
|
||||
"matrix-js-sdk": "41.0.0",
|
||||
"matrix-widget-api": "^1.16.1",
|
||||
"memoize-one": "^6.0.0",
|
||||
"mime": "^4.0.4",
|
||||
"oidc-client-ts": "^3.0.1",
|
||||
"opus-recorder": "^8.0.3",
|
||||
"pako": "^2.0.3",
|
||||
"png-chunks-extract": "^1.0.0",
|
||||
"posthog-js": "1.313.0",
|
||||
"posthog-js": "1.336.4",
|
||||
"qrcode": "1.5.4",
|
||||
"re-resizable": "6.11.2",
|
||||
"react": "^19.0.0",
|
||||
"react": "catalog:",
|
||||
"react-beautiful-dnd": "^13.1.0",
|
||||
"react-blurhash": "^0.3.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-dom": "catalog:",
|
||||
"react-focus-lock": "^2.5.1",
|
||||
"react-merge-refs": "^3.0.2",
|
||||
"react-string-replace": "^1.1.1",
|
||||
"react-string-replace": "^2.0.0",
|
||||
"react-transition-group": "^4.4.1",
|
||||
"react-virtuoso": "^4.14.0",
|
||||
"rfc4648": "^1.4.0",
|
||||
"sanitize-filename": "^1.6.3",
|
||||
"sanitize-html": "2.17.0",
|
||||
"tar-js": "^0.3.0",
|
||||
"temporal-polyfill": "^0.3.0",
|
||||
"ua-parser-js": "1.0.40",
|
||||
"uuid": "^13.0.0",
|
||||
"what-input": "^5.2.10"
|
||||
@@ -181,13 +149,15 @@
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@casualbot/jest-sonar-reporter": "2.5.0",
|
||||
"@element-hq/element-call-embedded": "0.16.3",
|
||||
"@element-hq/element-web-playwright-common": "2.2.3",
|
||||
"@element-hq/element-web-playwright-common": "catalog:",
|
||||
"@element-hq/element-web-playwright-common-local": "workspace:*",
|
||||
"@fetch-mock/jest": "^0.2.20",
|
||||
"@jest/globals": "^30.2.0",
|
||||
"@nx/jest": "^22.5.0",
|
||||
"@peculiar/webcrypto": "^1.4.3",
|
||||
"@playwright/test": "1.57.0",
|
||||
"@playwright/test": "catalog:",
|
||||
"@principalstudio/html-webpack-inject-preload": "^1.2.7",
|
||||
"@sentry/webpack-plugin": "^4.0.0",
|
||||
"@storybook/react-vite": "^10.0.7",
|
||||
"@stylistic/eslint-plugin": "^5.0.0",
|
||||
"@svgr/webpack": "^8.0.0",
|
||||
"@testing-library/dom": "^10.4.0",
|
||||
@@ -206,16 +176,16 @@
|
||||
"@types/jest": "30.0.0",
|
||||
"@types/jitsi-meet": "^2.0.2",
|
||||
"@types/jsrsasign": "^10.5.4",
|
||||
"@types/katex": "^0.16.0",
|
||||
"@types/lodash": "^4.14.168",
|
||||
"@types/minimist": "^1.2.5",
|
||||
"@types/modernizr": "^3.5.3",
|
||||
"@types/node": "18",
|
||||
"@types/node": "22",
|
||||
"@types/pako": "^2.0.0",
|
||||
"@types/postcss-import": "^14.0.3",
|
||||
"@types/qrcode": "^1.3.5",
|
||||
"@types/react": "19.2.7",
|
||||
"@types/react": "catalog:",
|
||||
"@types/react-beautiful-dnd": "^13.0.0",
|
||||
"@types/react-dom": "19.2.3",
|
||||
"@types/react-dom": "catalog:",
|
||||
"@types/react-transition-group": "^4.4.0",
|
||||
"@types/sanitize-html": "2.16.0",
|
||||
"@types/sdp-transform": "^2.4.10",
|
||||
@@ -229,7 +199,6 @@
|
||||
"babel-plugin-jsx-remove-data-test-id": "^3.0.0",
|
||||
"blob-polyfill": "^9.0.0",
|
||||
"chokidar": "^5.0.0",
|
||||
"concurrently": "^9.0.0",
|
||||
"copy-webpack-plugin": "^13.0.0",
|
||||
"cronstrue": "^3.0.0",
|
||||
"css-loader": "^7.0.0",
|
||||
@@ -262,36 +231,34 @@
|
||||
"jsqr": "^1.4.0",
|
||||
"knip": "^5.36.2",
|
||||
"lint-staged": "^16.0.0",
|
||||
"matrix-web-i18n": "3.5.2",
|
||||
"mini-css-extract-plugin": "2.9.2",
|
||||
"matrix-web-i18n": "catalog:",
|
||||
"mini-css-extract-plugin": "2.10.0",
|
||||
"minimist": "^1.2.6",
|
||||
"modernizr": "^3.12.0",
|
||||
"patch-package": "^8.0.0",
|
||||
"playwright-core": "^1.51.0",
|
||||
"postcss": "8.4.46",
|
||||
"postcss-easings": "^4.0.0",
|
||||
"nx": "22.5.0",
|
||||
"postcss": "8.5.6",
|
||||
"postcss-easings": "4.0.0",
|
||||
"postcss-hexrgba": "2.1.0",
|
||||
"postcss-import": "16.1.0",
|
||||
"postcss-loader": "8.1.1",
|
||||
"postcss-mixins": "^12.0.0",
|
||||
"postcss-nested": "^7.0.0",
|
||||
"postcss-preset-env": "^10.0.0",
|
||||
"postcss-scss": "^4.0.4",
|
||||
"postcss-simple-vars": "^7.0.1",
|
||||
"prettier": "3.7.4",
|
||||
"postcss-import": "16.1.1",
|
||||
"postcss-loader": "8.2.0",
|
||||
"postcss-mixins": "12.0.0",
|
||||
"postcss-nested": "7.0.2",
|
||||
"postcss-preset-env": "11.1.1",
|
||||
"postcss-scss": "4.0.9",
|
||||
"postcss-simple-vars": "7.0.1",
|
||||
"prettier": "3.8.1",
|
||||
"process": "^0.11.10",
|
||||
"raw-loader": "^4.0.2",
|
||||
"rimraf": "^6.0.0",
|
||||
"semver": "^7.5.2",
|
||||
"source-map-loader": "^5.0.0",
|
||||
"storybook": "^10.0.7",
|
||||
"stylelint": "^16.23.0",
|
||||
"stylelint-config-standard": "^39.0.0",
|
||||
"stylelint-scss": "^6.0.0",
|
||||
"stylelint": "^17.0.0",
|
||||
"stylelint-config-standard": "^40.0.0",
|
||||
"stylelint-scss": "^7.0.0",
|
||||
"stylelint-value-no-unknown-custom-properties": "^6.0.1",
|
||||
"terser-webpack-plugin": "^5.3.9",
|
||||
"testcontainers": "^11.0.0",
|
||||
"typescript": "5.9.3",
|
||||
"typescript": "catalog:",
|
||||
"util": "^0.12.5",
|
||||
"web-streams-polyfill": "^4.0.0",
|
||||
"webpack": "^5.89.0",
|
||||
@@ -302,6 +269,37 @@
|
||||
"webpack-version-file-plugin": "^0.5.0",
|
||||
"yaml": "^2.3.3"
|
||||
},
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
"matrix-js-sdk"
|
||||
],
|
||||
"ignoredBuiltDependencies": [
|
||||
"@sentry/cli",
|
||||
"nx"
|
||||
],
|
||||
"patchedDependencies": {
|
||||
"@vector-im/matrix-wysiwyg": "patches/@vector-im__matrix-wysiwyg.patch",
|
||||
"react-blurhash": "patches/react-blurhash.patch",
|
||||
"linkify-html": "patches/linkify-html.patch",
|
||||
"@matrix-org/react-sdk-module-api": "patches/@matrix-org__react-sdk-module-api.patch",
|
||||
"@types/mdx": "patches/@types__mdx.patch",
|
||||
"await-lock": "patches/await-lock.patch",
|
||||
"jest-fixed-jsdom": "patches/jest-fixed-jsdom.patch",
|
||||
"jsdom": "patches/jsdom.patch",
|
||||
"rollup": "patches/rollup.patch"
|
||||
},
|
||||
"peerDependencyRules": {
|
||||
"allowedVersions": {
|
||||
"react": "19",
|
||||
"react-dom": "19",
|
||||
"eslint": "8"
|
||||
}
|
||||
},
|
||||
"allowedDeprecatedVersions": {
|
||||
"eslint": "8",
|
||||
"react-beautiful-dnd": "13"
|
||||
}
|
||||
},
|
||||
"@casualbot/jest-sonar-reporter": {
|
||||
"outputDirectory": "coverage",
|
||||
"outputName": "jest-sonar-report.xml",
|
||||
@@ -310,5 +308,6 @@
|
||||
"engines": {
|
||||
"node": ">=22.18"
|
||||
},
|
||||
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
||||
"packageManager": "pnpm@10.28.2+sha512.41872f037ad22f7348e3b1debbaf7e867cfd448f2726d9cf74c08f19507c31d2c8e7a11525b983febc2df640b5438dee6023ebb1f84ed43cc2d654d2bc326264",
|
||||
"private": true
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
ARG PLAYWRIGHT_VERSION
|
||||
FROM mcr.microsoft.com/playwright:v${PLAYWRIGHT_VERSION}-noble
|
||||
|
||||
WORKDIR /work
|
||||
|
||||
# fonts-dejavu is needed for the same RTL rendering as on CI
|
||||
RUN apt-get update && apt-get -y install docker.io fonts-dejavu
|
||||
# Install the matching playwright runtime, the docker image only includes browsers
|
||||
RUN npm i -g playwright@${PLAYWRIGHT_VERSION}
|
||||
|
||||
COPY docker-entrypoint.sh /docker-entrypoint.sh
|
||||
|
||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||
@@ -0,0 +1,22 @@
|
||||
# @element-hq/element-web-playwright-common
|
||||
|
||||
Set of Playwright utilities to make it easier to write tests for Element Web, Element Web Modules & Element Desktop.
|
||||
|
||||
# This is a partial clone of https://github.com/element-hq/element-modules/tree/main/packages/element-web-playwright-common
|
||||
|
||||
In the future the rest of the package will be brought into this monorepo, for now it serves as an experimental alternative to https://github.com/element-hq/element-modules/pull/188
|
||||
|
||||
## Releases
|
||||
|
||||
The API is versioned using semver, with the major version incremented for breaking changes.
|
||||
|
||||
## Copyright & License
|
||||
|
||||
Copyright (c) 2026 Element Creations Ltd
|
||||
|
||||
This software is multi licensed by Element Creations Ltd (Element). It can be used either:
|
||||
|
||||
(1) for free under the terms of the GNU Affero General Public License (as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version); OR
|
||||
|
||||
(2) under the terms of a paid-for Element Commercial License agreement between you and Element (the terms of which may vary depending on what you and Element have agreed to).
|
||||
Unless required by applicable law or agreed to in writing, software distributed under the Licenses is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the Licenses for the specific language governing permissions and limitations under the Licenses.
|
||||
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
|
||||
# We use npm here as we used `npm i -g` to install playwright in the Dockerfile
|
||||
npm exec -- playwright run-server --port "$PORT" --host 0.0.0.0
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "@element-hq/element-web-playwright-common-local",
|
||||
"type": "module",
|
||||
"version": "3.0.0",
|
||||
"license": "SEE LICENSE IN README.md",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/element-hq/element-web.git",
|
||||
"directory": "packages/playwright-common"
|
||||
},
|
||||
"author": "element-hq",
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"playwright-screenshots-experimental": "playwright-screenshots.sh"
|
||||
},
|
||||
"devDependencies": {
|
||||
"wait-on": "^9.0.4"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# Handle symlinks here as we tend to be executed as an npm binary
|
||||
SCRIPT_PATH=$(readlink -f "$0")
|
||||
SCRIPT_DIR=$(dirname "$SCRIPT_PATH")
|
||||
|
||||
IMAGE_NAME="element-web-playwright-server"
|
||||
WS_PORT=3000
|
||||
|
||||
# Check the playwright version
|
||||
PW_VERSION=$(npm exec --silent -- playwright --version | gcut -d" " -f2)
|
||||
echo "Building $IMAGE_NAME:$PW_VERSION image in $SCRIPT_DIR"
|
||||
|
||||
# Build the image
|
||||
docker build -t "$IMAGE_NAME" --build-arg "PLAYWRIGHT_VERSION=$PW_VERSION" "$SCRIPT_DIR"
|
||||
|
||||
# Start the playwright-server in docker
|
||||
CONTAINER=$(docker run --network=host --rm -d -e PORT="$WS_PORT" "$IMAGE_NAME")
|
||||
# Set up an exit trap to clean up the docker container
|
||||
clean_up() {
|
||||
ARG=$?
|
||||
echo "Stopping playwright-server"
|
||||
docker stop "$CONTAINER" > /dev/null
|
||||
exit $ARG
|
||||
}
|
||||
trap clean_up EXIT
|
||||
|
||||
# Wait for playwright-server to be ready
|
||||
echo "Waiting for playwright-server"
|
||||
pnpm wait-on "tcp:$WS_PORT"
|
||||
|
||||
# Run the test we were given, setting PW_TEST_CONNECT_WS_ENDPOINT accordingly
|
||||
echo "Running '$@'"
|
||||
PW_TEST_CONNECT_WS_ENDPOINT="http://localhost:$WS_PORT" "$@"
|
||||
@@ -30,6 +30,18 @@ module.exports = {
|
||||
"react/jsx-key": ["error"],
|
||||
"matrix-org/require-copyright-header": "error",
|
||||
"react-compiler/react-compiler": "error",
|
||||
"no-restricted-imports": [
|
||||
"error",
|
||||
{
|
||||
paths: [
|
||||
{
|
||||
name: "react",
|
||||
importNames: ["act"],
|
||||
message: "Please use @test-utils instead.",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
@@ -0,0 +1,14 @@
|
||||
# Ignore test failure screenshots
|
||||
/src/**/__screenshots__/
|
||||
|
||||
# Ignore vis diffs & local baseline
|
||||
/__vis__/**/__diffs__
|
||||
/__vis__/**/__results__
|
||||
/__vis__/local
|
||||
|
||||
# Ignore coverage report
|
||||
/coverage/
|
||||
# Ignore generated docs
|
||||
typedoc
|
||||
# Build storybook
|
||||
storybook-static
|
||||
@@ -1,2 +0,0 @@
|
||||
dist/
|
||||
i18n/i18nKeys.d.ts
|
||||
@@ -21,8 +21,7 @@ export default create({
|
||||
// Toolbar
|
||||
barBg: "#ffffff",
|
||||
|
||||
brandTitle: "Element Web",
|
||||
brandUrl: "https://github.com/element-hq/element-web",
|
||||
brandImage: "https://element.io/images/logo-ele-secondary.svg",
|
||||
brandTitle: "Web Shared Components",
|
||||
brandUrl: "https://github.com/element-hq/element-web/tree/develop/packages/shared-components",
|
||||
brandTarget: "_self",
|
||||
});
|
||||
|
||||
@@ -5,8 +5,5 @@
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
.mx_RoomListSecondaryFilters {
|
||||
font: var(--cpd-font-body-md-medium);
|
||||
margin: var(--cpd-space-2x);
|
||||
margin-left: var(--cpd-space-1x);
|
||||
}
|
||||
@import url("@vector-im/compound-design-tokens/assets/web/css/compound-design-tokens.css") layer(compound);
|
||||
@import url("@vector-im/compound-web/dist/style.css");
|
||||
@@ -10,7 +10,7 @@ import { WithTooltip, IconButton, TooltipLinkList } from "storybook/internal/com
|
||||
import React from "react";
|
||||
import { GlobeIcon } from "@storybook/icons";
|
||||
|
||||
const languages = JSON.parse(process.env.STORYBOOK_LANGUAGES);
|
||||
const languages: string[] = JSON.parse(process.env.STORYBOOK_LANGUAGES!);
|
||||
|
||||
/**
|
||||
* Returns the title of a language in the user's locale.
|
||||
|
||||
@@ -6,17 +6,43 @@ Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import type { StorybookConfig } from "@storybook/react-vite";
|
||||
import path from "node:path";
|
||||
import fs from "node:fs";
|
||||
import { nodePolyfills } from "vite-plugin-node-polyfills";
|
||||
import { mergeConfig } from "vite";
|
||||
import { dirname } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
// Get a list of available languages so the language selector can display them at runtime
|
||||
const languages = fs.readdirSync("src/i18n/strings").map((f) => f.slice(0, -5));
|
||||
const languageFiles = fs.readdirSync("src/i18n/strings").map((f) => f.slice(0, -5));
|
||||
|
||||
const languages: Record<string, string> = {};
|
||||
for (const lang of languageFiles) {
|
||||
const normalizedLanguage = lang.toLowerCase().replace("_", "-");
|
||||
const languageParts = normalizedLanguage.split("-");
|
||||
if (languageParts.length === 2 && languageParts[0] === languageParts[1]) {
|
||||
languages[languageParts[0]] = `${lang}.json`;
|
||||
} else {
|
||||
languages[normalizedLanguage] = `${lang}.json`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is used to resolve the absolute path of a package.
|
||||
* It is needed in projects that use Yarn PnP or are set up within a monorepo.
|
||||
*/
|
||||
function getAbsolutePath(value: string): any {
|
||||
return dirname(fileURLToPath(import.meta.resolve(`${value}/package.json`)));
|
||||
}
|
||||
|
||||
const config: StorybookConfig = {
|
||||
stories: ["../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
|
||||
addons: ["@storybook/addon-docs", "@storybook/addon-designs", "@storybook/addon-a11y"],
|
||||
addons: [
|
||||
"@storybook/addon-docs",
|
||||
"@storybook/addon-designs",
|
||||
"@storybook/addon-a11y",
|
||||
"@storybook/addon-vitest",
|
||||
getAbsolutePath("storybook-addon-vis"),
|
||||
],
|
||||
framework: "@storybook/react-vite",
|
||||
core: {
|
||||
disableTelemetry: true,
|
||||
@@ -26,34 +52,17 @@ const config: StorybookConfig = {
|
||||
},
|
||||
async viteFinal(config) {
|
||||
return mergeConfig(config, {
|
||||
resolve: {
|
||||
alias: {
|
||||
// Alias used by i18n.tsx
|
||||
$webapp: path.resolve("../../webapp"),
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
// Needed for counterpart to work
|
||||
nodePolyfills({ include: ["process", "util"] }),
|
||||
nodePolyfills({ include: ["util"], globals: { global: false } }),
|
||||
{
|
||||
name: "language-middleware",
|
||||
configureServer(server) {
|
||||
server.middlewares.use((req, res, next) => {
|
||||
if (req.url === "/i18n/languages.json") {
|
||||
// Dynamically generate a languages.json file based on what files are available
|
||||
const langJson: Record<string, string> = {};
|
||||
for (const lang of languages) {
|
||||
const normalizedLanguage = lang.toLowerCase().replace("_", "-");
|
||||
const languageParts = normalizedLanguage.split("-");
|
||||
if (languageParts.length === 2 && languageParts[0] === languageParts[1]) {
|
||||
langJson[languageParts[0]] = `${lang}.json`;
|
||||
} else {
|
||||
langJson[normalizedLanguage] = `${lang}.json`;
|
||||
}
|
||||
}
|
||||
|
||||
res.setHeader("Content-Type", "application/json");
|
||||
res.end(JSON.stringify(langJson));
|
||||
res.end(JSON.stringify(languages));
|
||||
} else if (req.url?.startsWith("/i18n/")) {
|
||||
// Serve the individual language files, which annoyingly can't be a simple
|
||||
// static dir because the directory structure in src doesn't match what
|
||||
@@ -81,7 +90,7 @@ const config: StorybookConfig = {
|
||||
},
|
||||
env: (config) => ({
|
||||
...config,
|
||||
STORYBOOK_LANGUAGES: JSON.stringify(languages),
|
||||
STORYBOOK_LANGUAGES: JSON.stringify(Object.keys(languages)),
|
||||
}),
|
||||
};
|
||||
export default config;
|
||||
|
||||
@@ -8,3 +8,29 @@ Please see LICENSE files in the repository root for full details.
|
||||
.docs-story {
|
||||
background: var(--cpd-color-bg-canvas-default);
|
||||
}
|
||||
|
||||
/* Username color classes - these are defined in the main app's _common.pcss
|
||||
but need to be available in Storybook for components that use colorClass */
|
||||
.mx_Username_color1 {
|
||||
color: var(--cpd-color-text-decorative-1);
|
||||
}
|
||||
|
||||
.mx_Username_color2 {
|
||||
color: var(--cpd-color-text-decorative-2);
|
||||
}
|
||||
|
||||
.mx_Username_color3 {
|
||||
color: var(--cpd-color-text-decorative-3);
|
||||
}
|
||||
|
||||
.mx_Username_color4 {
|
||||
color: var(--cpd-color-text-decorative-4);
|
||||
}
|
||||
|
||||
.mx_Username_color5 {
|
||||
color: var(--cpd-color-text-decorative-5);
|
||||
}
|
||||
|
||||
.mx_Username_color6 {
|
||||
color: var(--cpd-color-text-decorative-6);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { ArgTypes, Preview, Decorator, ReactRenderer, StrictArgs } from "@storybook/react-vite";
|
||||
|
||||
import "../../../res/css/shared.pcss";
|
||||
import "./compound.css";
|
||||
import "./preview.css";
|
||||
import React, { useLayoutEffect } from "react";
|
||||
import { setLanguage } from "../src/utils/i18n";
|
||||
@@ -80,7 +80,7 @@ const withI18nProvider: Decorator = (Story) => {
|
||||
};
|
||||
|
||||
const preview: Preview = {
|
||||
tags: ["autodocs"],
|
||||
tags: ["autodocs", "snapshot"],
|
||||
decorators: [withThemeProvider, withTooltipProvider, withI18nProvider],
|
||||
parameters: {
|
||||
options: {
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
Copyright 2025 New Vector 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 { waitForPageReady, TestRunnerConfig } from "@storybook/test-runner";
|
||||
import { toMatchImageSnapshot } from "jest-image-snapshot";
|
||||
|
||||
const customSnapshotsDir = `${process.cwd()}/playwright/snapshots/`;
|
||||
const customReceivedDir = `${process.cwd()}/playwright/received/`;
|
||||
|
||||
const config: TestRunnerConfig = {
|
||||
setup() {
|
||||
expect.extend({ toMatchImageSnapshot });
|
||||
},
|
||||
async postVisit(page, context) {
|
||||
await waitForPageReady(page);
|
||||
|
||||
// If you want to take screenshot of multiple browsers, use
|
||||
// page.context().browser().browserType().name() to get the browser name to prefix the file name
|
||||
const image = await page.screenshot({ animations: "disabled" });
|
||||
expect(image).toMatchImageSnapshot({
|
||||
customSnapshotsDir,
|
||||
customSnapshotIdentifier: `${context.id}-${process.platform}`,
|
||||
storeReceivedOnFailure: true,
|
||||
customReceivedDir,
|
||||
customDiffDir: customReceivedDir,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
Copyright 2026 Element Creations Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import * as a11yAddonAnnotations from "@storybook/addon-a11y/preview";
|
||||
import { setProjectAnnotations } from "@storybook/react-vite";
|
||||
import { vis, visAnnotations } from "storybook-addon-vis/vitest-setup";
|
||||
|
||||
import * as projectAnnotations from "./preview.tsx";
|
||||
|
||||
// This is an important step to apply the right configuration when testing your stories.
|
||||
// More info at: https://storybook.js.org/docs/api/portable-stories/portable-stories-vitest#setprojectannotations
|
||||
setProjectAnnotations([a11yAddonAnnotations, projectAnnotations, visAnnotations]);
|
||||
|
||||
vis.setup({
|
||||
async auto() {
|
||||
const style = document.createElement("style");
|
||||
style.setAttribute("type", "text/css");
|
||||
style.appendChild(
|
||||
document.createTextNode(`
|
||||
/* Inhibit all animations for the screenshot to be more stable */
|
||||
*, *::before, *::after {
|
||||
animation: none !important;
|
||||
}
|
||||
/* Hide all storybook elements */
|
||||
.sb-wrapper {
|
||||
visibility: hidden !important;
|
||||
}
|
||||
`),
|
||||
);
|
||||
document.head.appendChild(style);
|
||||
},
|
||||
});
|
||||
@@ -1,5 +1,7 @@
|
||||
# @element-hq/web-shared-components
|
||||
|
||||
[Online storybook](https://shared-components-storybook.element.dev)
|
||||
|
||||
Shared React components library for Element Web, Aurora, Element
|
||||
modules... This package provides opinionated UI components built on top of the
|
||||
[Compound Design System](https://compound.element.io) and [Compound
|
||||
@@ -14,8 +16,8 @@ When adding this library to a new project, as well as installing
|
||||
dependency:
|
||||
|
||||
```bash
|
||||
yarn add @element-hq/web-shared-components
|
||||
yarn add @vector-im/compound-web
|
||||
pnpm add @element-hq/web-shared-components
|
||||
pnpm add @vector-im/compound-web
|
||||
```
|
||||
|
||||
(This avoids problems where we end up with different versions of compound-web in the
|
||||
@@ -97,22 +99,22 @@ function MyApp() {
|
||||
### Prerequisites
|
||||
|
||||
- Node.js >= 20.0.0
|
||||
- Yarn 1.22.22+
|
||||
- pnpm => 10
|
||||
|
||||
### Setup
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
yarn install
|
||||
pnpm install
|
||||
|
||||
# Build the library
|
||||
yarn prepare
|
||||
pnpm prepare
|
||||
```
|
||||
|
||||
### Running Storybook
|
||||
|
||||
```bash
|
||||
yarn storybook
|
||||
pnpm storybook
|
||||
```
|
||||
|
||||
### Write components
|
||||
@@ -121,45 +123,230 @@ Most components should be written as [MVVM pattern](../../docs/MVVM.md) view
|
||||
components. See existing components for examples. The exceptions are low level
|
||||
components that don't need a view model.
|
||||
|
||||
### Write Storybook Stories
|
||||
|
||||
All components should have accompanying Storybook stories for documentation and visual testing. Stories are written in TypeScript using the [Component Story Format (CSF)](https://storybook.js.org/docs/api/csf).
|
||||
|
||||
#### Story File Structure
|
||||
|
||||
Place the story file next to the component with the `.stories.tsx` extension:
|
||||
|
||||
```
|
||||
MyComponent/
|
||||
├── MyComponent.tsx
|
||||
├── MyComponent.module.css
|
||||
└── MyComponent.stories.tsx
|
||||
```
|
||||
|
||||
#### Regular Component Stories
|
||||
|
||||
For regular React components (non-MVVM), create stories by defining a meta object and story variations:
|
||||
|
||||
```tsx
|
||||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { fn } from "storybook/test";
|
||||
import { MyComponent } from "./MyComponent";
|
||||
|
||||
const meta = {
|
||||
title: "Category/MyComponent",
|
||||
component: MyComponent,
|
||||
tags: ["autodocs"],
|
||||
args: {
|
||||
// Default args for all stories
|
||||
label: "Default Label",
|
||||
onClick: fn(), // Mock function for tracking interactions
|
||||
},
|
||||
} satisfies Meta<typeof MyComponent>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
// Default story uses the default args
|
||||
export const Default: Story = {};
|
||||
|
||||
// Override specific args for variations
|
||||
export const WithCustomLabel: Story = {
|
||||
args: {
|
||||
label: "Custom Label",
|
||||
},
|
||||
};
|
||||
|
||||
export const Disabled: Story = {
|
||||
args: {
|
||||
disabled: true,
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
#### MVVM Component Stories
|
||||
|
||||
For MVVM components, create a wrapper component that uses `useMockedViewModel`:
|
||||
|
||||
```tsx
|
||||
import React, { type JSX } from "react";
|
||||
import { fn } from "storybook/test";
|
||||
import type { Meta, StoryFn } from "@storybook/react-vite";
|
||||
import { MyComponentView, type MyComponentViewSnapshot, type MyComponentViewActions } from "./MyComponentView";
|
||||
import { useMockedViewModel } from "../../useMockedViewModel";
|
||||
|
||||
// Combine snapshot and actions for easier typing
|
||||
type MyComponentProps = MyComponentViewSnapshot & MyComponentViewActions;
|
||||
|
||||
// Wrapper component that creates a mocked ViewModel
|
||||
const MyComponentViewWrapper = ({ onAction, ...rest }: MyComponentProps): JSX.Element => {
|
||||
const vm = useMockedViewModel(rest, {
|
||||
onAction,
|
||||
});
|
||||
return <MyComponentView vm={vm} />;
|
||||
};
|
||||
|
||||
export default {
|
||||
title: "Category/MyComponentView",
|
||||
component: MyComponentViewWrapper,
|
||||
tags: ["autodocs"],
|
||||
args: {
|
||||
// Snapshot properties (state)
|
||||
title: "Default Title",
|
||||
isLoading: false,
|
||||
// Action properties (callbacks)
|
||||
onAction: fn(),
|
||||
},
|
||||
} as Meta<typeof MyComponentViewWrapper>;
|
||||
|
||||
const Template: StoryFn<typeof MyComponentViewWrapper> = (args) => <MyComponentViewWrapper {...args} />;
|
||||
|
||||
export const Default = Template.bind({});
|
||||
|
||||
export const Loading = Template.bind({});
|
||||
Loading.args = {
|
||||
isLoading: true,
|
||||
};
|
||||
```
|
||||
|
||||
Thanks to this approach, we can directly use primitives in the story arguments instead of a view model object.
|
||||
|
||||
#### Linking Figma Designs
|
||||
|
||||
This package uses [@storybook/addon-designs](https://github.com/storybookjs/addon-designs) to embed Figma designs directly in Storybook. This helps developers compare their implementation with the design specs.
|
||||
|
||||
1. **Get the Figma URL**: Open your design in Figma, click "Share" → "Copy link"
|
||||
2. **Add to story parameters**: Include the `design` object in the meta's `parameters`
|
||||
3. **Supported URL formats**:
|
||||
- File links: `https://www.figma.com/file/...`
|
||||
- Design links: `https://www.figma.com/design/...`
|
||||
- Specific node: `https://www.figma.com/design/...?node-id=123-456`
|
||||
|
||||
Example with Figma integration:
|
||||
|
||||
```tsx
|
||||
export default {
|
||||
title: "Room List/RoomListSearchView",
|
||||
component: RoomListSearchViewWrapper,
|
||||
tags: ["autodocs"],
|
||||
args: {
|
||||
// ... your args
|
||||
},
|
||||
parameters: {
|
||||
design: {
|
||||
type: "figma",
|
||||
url: "https://www.figma.com/design/vlmt46QDdE4dgXDiyBJXqp/ER-33-Left-Panel?node-id=98-1979",
|
||||
},
|
||||
},
|
||||
} as Meta<typeof RoomListSearchViewWrapper>;
|
||||
```
|
||||
|
||||
The Figma design will appear in the "Design" tab in Storybook.
|
||||
|
||||
#### Non-UI Utility Stories
|
||||
|
||||
For utility functions, helpers, and other non-UI exports, create documentation stories using TSX format with TypeDoc-generated markdown.
|
||||
|
||||
`src/utils/humanize.stories.tsx`
|
||||
|
||||
```tsx
|
||||
import React from "react";
|
||||
import { Markdown } from "@storybook/addon-docs/blocks";
|
||||
|
||||
import type { Meta } from "@storybook/react-vite";
|
||||
import humanizeTimeDoc from "../../typedoc/functions/humanizeTime.md?raw";
|
||||
|
||||
const meta = {
|
||||
title: "utils/humanize",
|
||||
parameters: {
|
||||
docs: {
|
||||
page: () => (
|
||||
<>
|
||||
<h1>humanize</h1>
|
||||
<Markdown>{humanizeTimeDoc}</Markdown>
|
||||
</>
|
||||
),
|
||||
},
|
||||
},
|
||||
tags: ["autodocs", "skip-test"],
|
||||
} satisfies Meta;
|
||||
|
||||
export default meta;
|
||||
|
||||
// Docs-only story - renders nothing but triggers autodocs
|
||||
export const Docs = {
|
||||
render: () => null,
|
||||
};
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> Be sure to include the `skip-test` tag in your utility stories to prevent them from running as visual tests.
|
||||
|
||||
**Workflow:**
|
||||
|
||||
1. Write TsDoc in your utility function
|
||||
2. Export the function from `src/index.ts`
|
||||
3. Run `pnpm build:doc` to generate TypeDoc markdown
|
||||
4. Create a `.stories.tsx` file importing the generated markdown
|
||||
5. The documentation appears automatically in Storybook
|
||||
|
||||
### Tests
|
||||
|
||||
Two types of tests are available: unit tests and visual regression tests.
|
||||
|
||||
### Unit Tests
|
||||
|
||||
These tests cover the logic of the components and utilities. Built with Jest
|
||||
These tests cover the logic of the components and utilities. Built with Vitest
|
||||
and React Testing Library.
|
||||
|
||||
```bash
|
||||
yarn test
|
||||
pnpm test:unit
|
||||
```
|
||||
|
||||
### Visual Regression Tests
|
||||
|
||||
These tests ensure the UI components render correctly. They need Storybook to
|
||||
be running and they will run in docker using [Playwright](../../playwright.md).
|
||||
|
||||
First run storybook:
|
||||
These tests ensure the UI components render correctly.
|
||||
Built with Storybook and run under vitest using playwright.
|
||||
|
||||
```bash
|
||||
yarn storybook
|
||||
```
|
||||
|
||||
Then, in another terminal, run:
|
||||
|
||||
```bash
|
||||
yarn test:storybook:update
|
||||
pnpm test:storybook:update
|
||||
```
|
||||
|
||||
Each story will be rendered and a screenshot will be taken and compared to the
|
||||
existing baseline. If there are visual changes or AXE violation, the test will
|
||||
fail.
|
||||
|
||||
Screenshots are located in `packages/shared-components/__vis__/`.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> In case of docker issues with Playwright, see [playwright EW documentation](https://github.com/element-hq/element-web/blob/develop/docs/playwright.md#supported-container-runtimes).
|
||||
|
||||
### Translations
|
||||
|
||||
First see our [translation guide](../../docs/translation.md) and [translation dev guide](../../docs/translation-dev.md).
|
||||
First see our [translation guide](../../docs/translating.md) and [translation dev guide](../../docs/translating-dev.md).
|
||||
To generate translation strings for this package, run:
|
||||
|
||||
```bash
|
||||
yarn i18n
|
||||
pnpm i18n
|
||||
```
|
||||
|
||||
## Publish a new version
|
||||
|
||||
Two steps are required to publish a new version of this package:
|
||||
|
||||
1. Bump the version in `package.json` following semver rules and open a PR.
|
||||
2. Once merged run the [github workflow](https://github.com/element-hq/element-web/actions/workflows/shared-component-publish.yaml)
|
||||
|
||||
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 21 KiB |