Compare commits
179 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d7f94f89dc | |||
| 8a21062476 | |||
| 0575ca9598 | |||
| 6bc5036042 | |||
| 7d9086f767 | |||
| 92615272ce | |||
| 1b082a248c | |||
| 7c3cf696fd | |||
| 0053bedc38 | |||
| 674d64ac12 | |||
| 7dbffb348d | |||
| 25d24d478f | |||
| 62c7fe5408 | |||
| 8cef5df140 | |||
| 8381c435a1 | |||
| e07e26cae5 | |||
| 24018f7e94 | |||
| b31527261d | |||
| 8bb1cb5e63 | |||
| 8769165e88 | |||
| 4daf227137 | |||
| f0f93decfd | |||
| e5f0ea79e5 | |||
| b908e8c7f2 | |||
| dda87ff1ed | |||
| 617722018c | |||
| a972340216 | |||
| d5c49e6a2e | |||
| e0949e47c4 | |||
| 2ef02fa39e | |||
| 68cc500705 | |||
| 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 | |||
| 1ec05298cf | |||
| eaa34e6dfa | |||
| bc4be12790 | |||
| fefe093ce0 | |||
| ee4277fd95 | |||
| a15efcc6d0 | |||
| b7a2e8c64e | |||
| 93a8b67ed0 | |||
| 3b08b5c582 | |||
| f236c26356 | |||
| b8ad0b93db | |||
| 3f021472c2 | |||
| 1c42173e5b | |||
| 0b91be5a78 | |||
| c91a24f17b | |||
| 20de09e80f | |||
| 52748d6d35 | |||
| 92a6db5912 | |||
| d9a4858b1d | |||
| fbac316991 | |||
| b9638695b7 | |||
| e2dad68169 | |||
| 466f60ead5 | |||
| 28464b4d12 | |||
| 420576f4ae | |||
| e811cf0f84 | |||
| 7e5a3a530d | |||
| f9778fcbd3 | |||
| 575b3b5400 | |||
| cac682247c | |||
| 82b270616f | |||
| 6f0cd7621b | |||
| f5c6477ef7 | |||
| 1eb07ba750 | |||
| 45ed3c500b | |||
| 7f408bd6cf | |||
| 9cc80c5f36 | |||
| bdee36ca7c | |||
| 9640c330e5 | |||
| 33764d39ba | |||
| 775332b179 | |||
| b2a5f4d58b | |||
| 04800c15af | |||
| fb060721dc | |||
| f079224dd6 | |||
| 5e8024009c | |||
| 2e6cf8734b | |||
| d9be851965 | |||
| 2db10037f2 | |||
| 133951b663 | |||
| 06f70d1d7c | |||
| 7ad6b4b411 | |||
| 239527996a | |||
| 795780da66 | |||
| ce741f055c | |||
| 7d72775af9 | |||
| 540d71f49c | |||
| 220e93596a | |||
| 2c90eee2dd | |||
| edd4eab195 | |||
| 15c409491d | |||
| d060b77e8f | |||
| 6e9d168dd2 | |||
| cb7e8f4910 | |||
| a5af6cf23f | |||
| 9db08a4574 | |||
| 2f817f32ce | |||
| 13696af194 | |||
| 4ee04d0661 | |||
| 7f057faaad | |||
| d7a58216ae | |||
| 4dd128fd18 | |||
| 126b216d44 | |||
| b8ecc0e07e | |||
| 4668c15ea1 | |||
| 867a6850e4 | |||
| adc5ee22cc | |||
| aa6509e01c | |||
| 770ff42496 | |||
| db352ef876 | |||
| acca876697 | |||
| d9e7948920 | |||
| d7feaa0b2a | |||
| ebd7cdb09d | |||
| df091a7d3e | |||
| fea10b3c19 |
@@ -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
|
||||
@@ -25,7 +26,10 @@
|
||||
|
||||
# Ignore translations as those will be updated by GHA for Localazy download
|
||||
/src/i18n/strings
|
||||
/src/i18n/strings/en_EN.json @element-hq/element-web-reviewers
|
||||
/packages/shared-components/src/i18n/strings
|
||||
/src/i18n/strings/en_EN.json @element-hq/element-web-reviewers
|
||||
/packages/shared-components/src/i18n/strings/en_EN.json @element-hq/element-web-reviewers
|
||||
|
||||
# Ignore the synapse & mas plugins as this is updated by GHA for docker image updating
|
||||
/playwright/testcontainers/synapse.ts
|
||||
/playwright/testcontainers/mas.ts
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] I have read through [review guidelines](../docs/review.md) and [CONTRIBUTING.md](../CONTRIBUTING.md).
|
||||
- [ ] I have read through [review guidelines](https://github.com/element-hq/element-web/blob/develop/docs/review.md) and [CONTRIBUTING.md](https://github.com/element-hq/element-web/blob/develop/CONTRIBUTING.md).
|
||||
- [ ] Tests written for new code (and old code if feasible).
|
||||
- [ ] New or updated `public`/`exported` symbols have accurate [TSDoc](https://tsdoc.org/) documentation.
|
||||
- [ ] Linter and other CI checks pass.
|
||||
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
||||
|
||||
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
||||
- 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
|
||||
@@ -66,7 +66,7 @@ jobs:
|
||||
run: VERSION=$(scripts/get-version-from-git.sh) yarn build
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
||||
with:
|
||||
name: webapp-${{ matrix.image }}
|
||||
path: webapp
|
||||
|
||||
@@ -62,7 +62,7 @@ jobs:
|
||||
dpkg-gencontrol -v"$VERSION" -ldebian/tmp/DEBIAN/changelog
|
||||
dpkg-deb -Zxz --root-owner-group --build debian/tmp element-web.deb
|
||||
|
||||
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
|
||||
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
||||
with:
|
||||
name: element-web.deb
|
||||
path: element-web.deb
|
||||
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
||||
|
||||
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
with:
|
||||
cache: "yarn"
|
||||
node-version: "lts/*"
|
||||
@@ -53,7 +53,7 @@ jobs:
|
||||
|
||||
- run: mv dist/element-*.tar.gz dist/develop.tar.gz
|
||||
|
||||
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
|
||||
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
||||
with:
|
||||
name: webapp
|
||||
path: dist/develop.tar.gz
|
||||
@@ -104,7 +104,7 @@ jobs:
|
||||
running-workflow-name: "Build & Deploy develop.element.io"
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
wait-interval: 10
|
||||
check-regexp: ^((?!SonarCloud|SonarQube|issue|board|label|Release|prepare|GitHub Pages).)*$
|
||||
check-regexp: ^((?!SonarCloud|SonarQube|issue|board|label|Release|prepare|GitHub Pages|Upload).)*$
|
||||
|
||||
# We keep the latest develop.tar.gz on R2 instead of relying on the github artifact uploaded earlier
|
||||
# as the expires after 24h and requires auth to download.
|
||||
|
||||
@@ -32,25 +32,10 @@ jobs:
|
||||
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3
|
||||
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
|
||||
with:
|
||||
install: true
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # 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
|
||||
if: github.event_name != 'pull_request'
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and load
|
||||
id: test-build
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6
|
||||
@@ -102,12 +87,64 @@ jobs:
|
||||
images: |
|
||||
vectorim/element-web
|
||||
ghcr.io/element-hq/element-web
|
||||
oci-push.vpn.infra.element.io/element-web
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=tag
|
||||
flavor: |
|
||||
latest=${{ contains(github.ref_name, '-rc.') && 'false' || 'auto' }}
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # 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
|
||||
if: github.event_name != 'pull_request'
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Connect to Tailscale
|
||||
uses: tailscale/github-action@53acf823325fe9ca47f4cdaa951f90b4b0de5bb9 # v4
|
||||
if: github.event_name != 'pull_request'
|
||||
with:
|
||||
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
|
||||
audience: ${{ secrets.TS_AUDIENCE }}
|
||||
tags: tag:github-actions
|
||||
|
||||
- name: Compute vault jwt role name
|
||||
id: vault-jwt-role
|
||||
if: github.event_name != 'pull_request'
|
||||
run: |
|
||||
echo "role_name=github_service_management_$( echo "${{ github.repository }}" | sed -r 's|[/-]|_|g')" | tee -a "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Get team registry token
|
||||
id: import-secrets
|
||||
uses: hashicorp/vault-action@4c06c5ccf5c0761b6029f56cfb1dcf5565918a3b # v3
|
||||
if: github.event_name != 'pull_request'
|
||||
with:
|
||||
url: https://vault.infra.ci.i.element.dev
|
||||
role: ${{ steps.vault-jwt-role.outputs.role_name }}
|
||||
path: service-management/github-actions
|
||||
jwtGithubAudience: https://vault.infra.ci.i.element.dev
|
||||
method: jwt
|
||||
secrets: |
|
||||
services/web-repositories/secret/data/oci.element.io username | OCI_USERNAME ;
|
||||
services/web-repositories/secret/data/oci.element.io password | OCI_PASSWORD ;
|
||||
|
||||
- name: Login to oci.element.io Registry
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3
|
||||
if: github.event_name != 'pull_request'
|
||||
with:
|
||||
registry: oci-push.vpn.infra.element.io
|
||||
username: ${{ steps.import-secrets.outputs.OCI_USERNAME }}
|
||||
password: ${{ steps.import-secrets.outputs.OCI_PASSWORD }}
|
||||
|
||||
- name: Build and push
|
||||
id: build-and-push
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6
|
||||
@@ -139,16 +176,3 @@ jobs:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
repository: vectorim/element-web
|
||||
|
||||
- name: Repository Dispatch
|
||||
uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4
|
||||
if: github.event_name != 'pull_request'
|
||||
with:
|
||||
repository: element-hq/element-web-pro
|
||||
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
event-type: image-built
|
||||
# Stable way to determine the :version
|
||||
client-payload: |-
|
||||
{
|
||||
"base-ref": "${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }}"
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
repository: matrix-org/matrix-js-sdk
|
||||
path: matrix-js-sdk
|
||||
|
||||
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
with:
|
||||
cache: "yarn"
|
||||
cache-dependency-path: element-web/yarn.lock
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
actions: read
|
||||
steps:
|
||||
- name: Download HTML report
|
||||
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
run-id: ${{ github.event.workflow_run.id }}
|
||||
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
with:
|
||||
repository: element-hq/element-web
|
||||
|
||||
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
with:
|
||||
cache: "yarn"
|
||||
node-version: "lts/*"
|
||||
@@ -74,7 +74,7 @@ jobs:
|
||||
run: VERSION=$(scripts/get-version-from-git.sh) yarn build
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
||||
with:
|
||||
name: webapp
|
||||
path: webapp
|
||||
@@ -128,12 +128,12 @@ jobs:
|
||||
repository: element-hq/element-web
|
||||
|
||||
- name: 📥 Download artifact
|
||||
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
|
||||
with:
|
||||
name: webapp
|
||||
path: webapp
|
||||
|
||||
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
with:
|
||||
cache: "yarn"
|
||||
cache-dependency-path: yarn.lock
|
||||
@@ -147,7 +147,7 @@ jobs:
|
||||
run: echo "version=$(yarn list --pattern @playwright/test --depth=0 --json --non-interactive --no-progress | jq -r '.data.trees[].name')" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Cache playwright binaries
|
||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
|
||||
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5
|
||||
id: playwright-cache
|
||||
with:
|
||||
path: ~/.cache/ms-playwright
|
||||
@@ -172,7 +172,7 @@ jobs:
|
||||
|
||||
- name: Upload blob report to GitHub Actions Artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
||||
with:
|
||||
name: all-blob-reports-${{ matrix.project }}-${{ matrix.runner }}
|
||||
path: blob-report
|
||||
@@ -200,7 +200,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
repository: element-hq/element-web
|
||||
|
||||
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
if: inputs.skip != true
|
||||
with:
|
||||
cache: "yarn"
|
||||
@@ -212,7 +212,7 @@ jobs:
|
||||
|
||||
- name: Download blob reports from GitHub Actions Artifacts
|
||||
if: inputs.skip != true
|
||||
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
|
||||
with:
|
||||
pattern: all-blob-reports-*
|
||||
path: all-blob-reports
|
||||
@@ -228,7 +228,7 @@ jobs:
|
||||
# Upload the HTML report even if one of our reporters fails, this can happen when stale screenshots are detected
|
||||
- name: Upload HTML report
|
||||
if: always() && inputs.skip != true
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
||||
with:
|
||||
name: html-report
|
||||
path: playwright-report
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
Exercise caution. Use test accounts.
|
||||
|
||||
- name: 📥 Download artifact
|
||||
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
run-id: ${{ github.event.workflow_run.id }}
|
||||
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
|
||||
- name: Create Pull Request
|
||||
id: cpr
|
||||
uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676 # v7
|
||||
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # v8
|
||||
with:
|
||||
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
branch: actions/playwright-image-updates
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
||||
|
||||
- name: 🔧 Set up node environment
|
||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
with:
|
||||
cache: "yarn"
|
||||
node-version-file: ".node-version"
|
||||
@@ -37,4 +37,4 @@ jobs:
|
||||
|
||||
- name: 🚀 Publish to npm
|
||||
working-directory: packages/shared-components
|
||||
run: npm publish --access public --tag test --provenance
|
||||
run: npm publish --access public --provenance
|
||||
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
run: "sudo apt-get install -y tree"
|
||||
|
||||
- name: Download Diffs
|
||||
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
run-id: ${{ github.event.workflow_run.id }}
|
||||
|
||||
@@ -26,14 +26,11 @@ jobs:
|
||||
persist-credentials: false
|
||||
repository: element-hq/element-web
|
||||
|
||||
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
with:
|
||||
cache: "yarn"
|
||||
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
|
||||
@@ -44,7 +41,7 @@ jobs:
|
||||
run: echo "version=$(yarn list --pattern @playwright/test --depth=0 --json --non-interactive --no-progress | jq -r '.data.trees[].name')" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Cache playwright binaries
|
||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
|
||||
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5
|
||||
id: playwright-cache
|
||||
with:
|
||||
path: ~/.cache/ms-playwright
|
||||
@@ -56,11 +53,18 @@ jobs:
|
||||
run: "yarn 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: "yarn 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@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
||||
with:
|
||||
name: received-images
|
||||
path: packages/shared-components/playwright/shared-component-received
|
||||
path: packages/shared-components/__vis__/linux
|
||||
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
||||
|
||||
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
with:
|
||||
cache: "yarn"
|
||||
node-version: "lts/*"
|
||||
@@ -41,8 +41,8 @@ jobs:
|
||||
- name: Typecheck Shared Components
|
||||
run: "yarn --cwd packages/shared-components run lint:types"
|
||||
|
||||
i18n_lint:
|
||||
name: "i18n Check"
|
||||
i18n_lint_ew:
|
||||
name: "i18n Check (Element Web)"
|
||||
uses: matrix-org/matrix-web-i18n/.github/workflows/i18n_check.yml@main
|
||||
permissions:
|
||||
pull-requests: read
|
||||
@@ -59,6 +59,15 @@ jobs:
|
||||
devtools|settings|elementCallUrl
|
||||
labs|sliding_sync_description
|
||||
|
||||
i18n_lint_shared_components:
|
||||
name: "i18n Check (Shared Components)"
|
||||
uses: matrix-org/matrix-web-i18n/.github/workflows/i18n_check.yml@main
|
||||
permissions:
|
||||
pull-requests: read
|
||||
with:
|
||||
path: "packages/shared-components"
|
||||
hardcoded-words: "Element"
|
||||
|
||||
rethemendex_lint:
|
||||
name: "Rethemendex Check"
|
||||
runs-on: ubuntu-24.04
|
||||
@@ -75,7 +84,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
||||
|
||||
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
with:
|
||||
cache: "yarn"
|
||||
node-version: "lts/*"
|
||||
@@ -99,7 +108,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
||||
|
||||
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
with:
|
||||
cache: "yarn"
|
||||
node-version: "lts/*"
|
||||
@@ -117,7 +126,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
||||
|
||||
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
with:
|
||||
cache: "yarn"
|
||||
node-version: "lts/*"
|
||||
@@ -135,7 +144,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
||||
|
||||
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
with:
|
||||
cache: "yarn"
|
||||
node-version: "lts/*"
|
||||
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
repository: ${{ inputs.matrix-js-sdk-sha && 'element-hq/element-web' || github.repository }}
|
||||
|
||||
- name: Yarn cache
|
||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
with:
|
||||
node-version: "lts/*"
|
||||
cache: "yarn"
|
||||
@@ -55,7 +55,7 @@ jobs:
|
||||
JS_SDK_GITHUB_BASE_REF: ${{ inputs.matrix-js-sdk-sha }}
|
||||
|
||||
- name: Jest Cache
|
||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
|
||||
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5
|
||||
with:
|
||||
path: /tmp/jest_cache
|
||||
key: ${{ hashFiles('**/yarn.lock') }}
|
||||
@@ -84,7 +84,7 @@ jobs:
|
||||
|
||||
- name: Upload Artifact
|
||||
if: env.ENABLE_COVERAGE == 'true'
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
||||
with:
|
||||
name: coverage-${{ matrix.runner }}
|
||||
path: |
|
||||
@@ -93,13 +93,13 @@ 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
|
||||
@@ -113,8 +113,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
|
||||
@@ -123,43 +123,47 @@ jobs:
|
||||
repository: ${{ inputs.matrix-js-sdk-sha && 'element-hq/element-web' || github.repository }}
|
||||
|
||||
- name: Yarn cache
|
||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
with:
|
||||
node-version: "lts/*"
|
||||
cache: "yarn"
|
||||
|
||||
- name: Install EW Deps
|
||||
run: "yarn install"
|
||||
|
||||
- name: Install Shared Component Deps
|
||||
working-directory: "packages/shared-components"
|
||||
run: "yarn install"
|
||||
|
||||
- name: Jest Cache
|
||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
|
||||
- name: Cache storybook & vitest
|
||||
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # 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('packages/shared-components/yarn.lock') }}
|
||||
|
||||
- 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=$(yarn list --pattern @playwright/test --depth=0 --json --non-interactive --no-progress | jq -r '.data.trees[].name')" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Cache playwright binaries
|
||||
uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # 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: "yarn 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: yarn test:unit --coverage=${{ env.ENABLE_COVERAGE }}
|
||||
|
||||
- name: Upload Artifact
|
||||
if: env.ENABLE_COVERAGE == 'true'
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
||||
with:
|
||||
name: coverage-sharedcomponents
|
||||
path: |
|
||||
|
||||
@@ -9,7 +9,7 @@ jobs:
|
||||
name: Move PRs asking for design review to the design board
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: octokit/graphql-action@abaeca7ba4f0325d63b8de7ef943c2418d161b93 # v3.0.0
|
||||
- uses: octokit/graphql-action@ddde8ebb2493e79f390e6449c725c21663a67505 # v3.0.2
|
||||
id: find_team_members
|
||||
with:
|
||||
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
||||
@@ -52,7 +52,7 @@ jobs:
|
||||
fi
|
||||
env:
|
||||
TEAM: "design"
|
||||
- uses: octokit/graphql-action@abaeca7ba4f0325d63b8de7ef943c2418d161b93 # v3.0.0
|
||||
- uses: octokit/graphql-action@ddde8ebb2493e79f390e6449c725c21663a67505 # v3.0.2
|
||||
id: add_to_project
|
||||
if: steps.any_matching_reviewers.outputs.match == 'true'
|
||||
with:
|
||||
@@ -76,7 +76,7 @@ jobs:
|
||||
name: Move PRs asking for design review to the design board
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: octokit/graphql-action@abaeca7ba4f0325d63b8de7ef943c2418d161b93 # v3.0.0
|
||||
- uses: octokit/graphql-action@ddde8ebb2493e79f390e6449c725c21663a67505 # v3.0.2
|
||||
id: find_team_members
|
||||
with:
|
||||
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
||||
@@ -119,7 +119,7 @@ jobs:
|
||||
fi
|
||||
env:
|
||||
TEAM: "product"
|
||||
- uses: octokit/graphql-action@abaeca7ba4f0325d63b8de7ef943c2418d161b93 # v3.0.0
|
||||
- uses: octokit/graphql-action@ddde8ebb2493e79f390e6449c725c21663a67505 # v3.0.2
|
||||
id: add_to_project
|
||||
if: steps.any_matching_reviewers.outputs.match == 'true'
|
||||
with:
|
||||
|
||||
@@ -11,7 +11,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
||||
|
||||
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
with:
|
||||
cache: "yarn"
|
||||
node-version: "lts/*"
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
run: "yarn update:jitsi"
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676 # v7
|
||||
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # 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,6 @@ storybook-static
|
||||
/packages/shared-components/node_modules
|
||||
/packages/shared-components/dist
|
||||
/packages/shared-components/src/i18nKeys.d.ts
|
||||
|
||||
# TSC incremental compilation information
|
||||
*.tsbuildinfo
|
||||
|
||||
@@ -1,3 +1,67 @@
|
||||
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
|
||||
|
||||
* Allow local log downloads when a rageshake URL is not configured. ([#31716](https://github.com/element-hq/element-web/pull/31716)). Contributed by @Half-Shot.
|
||||
* Improve icon rendering accessibility ([#31776](https://github.com/element-hq/element-web/pull/31776)). Contributed by @t3chguy.
|
||||
* Show "Bob shared this message" on messages shared via MSC4268 ([#31684](https://github.com/element-hq/element-web/pull/31684)). Contributed by @richvdh.
|
||||
* Update the way we render icons for accessibility ([#31731](https://github.com/element-hq/element-web/pull/31731)). Contributed by @t3chguy.
|
||||
* Switch from css masks to rendering svg ([#31681](https://github.com/element-hq/element-web/pull/31681)). Contributed by @t3chguy.
|
||||
* Support for stable MSC4191 account management action parameter ([#31701](https://github.com/element-hq/element-web/pull/31701)). Contributed by @hughns.
|
||||
* Support for stable m.oauth UIA stage from MSC4312 ([#31704](https://github.com/element-hq/element-web/pull/31704)). Contributed by @hughns.
|
||||
* Switch to Compound icons to replace old icons ([#31667](https://github.com/element-hq/element-web/pull/31667)). Contributed by @t3chguy.
|
||||
* Switch from svg masks to svg rendering in more places ([#31652](https://github.com/element-hq/element-web/pull/31652)). Contributed by @t3chguy.
|
||||
* Switch from svg masks to svg rendering in more places ([#31650](https://github.com/element-hq/element-web/pull/31650)). Contributed by @t3chguy.
|
||||
* Update notification icons using Compound icons ([#31671](https://github.com/element-hq/element-web/pull/31671)). Contributed by @t3chguy.
|
||||
* Memoise ListView context ([#31668](https://github.com/element-hq/element-web/pull/31668)). Contributed by @t3chguy.
|
||||
* Switch emoji picker to use emoji for header icons ([#31645](https://github.com/element-hq/element-web/pull/31645)). Contributed by @t3chguy.
|
||||
* Replace icons with Compound alternatives ([#31642](https://github.com/element-hq/element-web/pull/31642)). Contributed by @t3chguy.
|
||||
|
||||
## 🐛 Bug Fixes
|
||||
|
||||
* Fix avatar decorations in thread activity centre not being atop avatar ([#31789](https://github.com/element-hq/element-web/pull/31789)). Contributed by @t3chguy.
|
||||
* Fix room settings roles tab getting confused if power level change fails ([#31768](https://github.com/element-hq/element-web/pull/31768)). Contributed by @t3chguy.
|
||||
* Custom themes now import highlights in css ([#31758](https://github.com/element-hq/element-web/pull/31758)). Contributed by @Philldomd.
|
||||
* Use correct translation for url preview settings ([#31740](https://github.com/element-hq/element-web/pull/31740)). Contributed by @florianduros.
|
||||
* Fix error shown if accepting a 3pid invite ([#31735](https://github.com/element-hq/element-web/pull/31735)). Contributed by @dbkr.
|
||||
* Ensure correct focus configuration for Element Call before allowing users to call. ([#31490](https://github.com/element-hq/element-web/pull/31490)). Contributed by @Half-Shot.
|
||||
* Fix emoji font in emoji picker header buttons ([#31679](https://github.com/element-hq/element-web/pull/31679)). Contributed by @t3chguy.
|
||||
* fix flaky test by waiting for chat panel before counting messages ([#31633](https://github.com/element-hq/element-web/pull/31633)). Contributed by @BillCarsonFr.
|
||||
|
||||
|
||||
Changes in [1.12.8](https://github.com/element-hq/element-web/releases/tag/v1.12.8) (2026-01-13)
|
||||
================================================================================================
|
||||
## 🦖 Deprecations
|
||||
|
||||
@@ -184,6 +184,16 @@ Please ensure your changes match the cosmetic style of the existing project,
|
||||
and **_never_** mix cosmetic and functional changes in the same commit, as it
|
||||
makes it horribly hard to review otherwise.
|
||||
|
||||
## Shared Components
|
||||
|
||||
When creating new UI components, consider whether they should be added to the shared components package (`packages/shared-components`) rather than directly in the main `src/` directory. Components should be placed in shared components if they:
|
||||
|
||||
- Are reusable across different parts of the application
|
||||
- Could potentially be used by other Element projects (Element Desktop, Aurora, Element modules...)
|
||||
- Follow established patterns and don't have tight coupling to specific application logic
|
||||
|
||||
For more details, see the [shared components README](./packages/shared-components/README.md).
|
||||
|
||||
## Attribution
|
||||
|
||||
Everyone who contributes anything to Matrix is welcome to be listed in the
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# syntax=docker.io/docker/dockerfile:1.20-labs@sha256:dbcde2ebc4abc8bb5c3c499b9c9a6876842bf5da243951cd2697f921a7aeb6a9
|
||||
|
||||
# Builder
|
||||
FROM --platform=$BUILDPLATFORM node:24-bullseye@sha256:5583cbe5d3347db372d9a9100eba272b548ca1f53246b9b769334bcd0eef2c26 AS builder
|
||||
FROM --platform=$BUILDPLATFORM node:24-bullseye@sha256:0cb1bd306f3bdb19174ea9dde765315532cb72074a549a1683fd7aacdde03a52 AS builder
|
||||
|
||||
# Support custom branch of the js-sdk. This also helps us build images of element-web develop.
|
||||
ARG USE_CUSTOM_SDKS=false
|
||||
@@ -19,7 +19,7 @@ RUN /src/scripts/docker-package.sh
|
||||
RUN cp /src/config.sample.json /src/webapp/config.json
|
||||
|
||||
# App
|
||||
FROM nginxinc/nginx-unprivileged:alpine-slim@sha256:a6bec37058b9047ece799c01d98dc6d5aa0542b6583cc69f187652f91331a752
|
||||
FROM nginxinc/nginx-unprivileged:alpine-slim@sha256:a75b70e1479178becce46b2028076899e648665b88fd685472469b34316356ec
|
||||
|
||||
# Need root user to install packages & manipulate the usr directory
|
||||
USER root
|
||||
|
||||
@@ -194,6 +194,17 @@ To add a new translation, head to the [translating doc](docs/translating.md).
|
||||
|
||||
For a developer guide, see the [translating dev doc](docs/translating-dev.md).
|
||||
|
||||
# Extending Element Web with Modules
|
||||
|
||||
Element Web supports a module system that allows you to extend or modify functionality at runtime. Modules are loaded dynamically and provide a safe, predictable API for customization.
|
||||
|
||||
## What are modules?
|
||||
|
||||
Modules are extensions that can add or modify Element Web's functionality. They are:
|
||||
|
||||
- Built using the [`@element-hq/element-web-module-api`](https://github.com/element-hq/element-modules/tree/main/packages/element-web-module-api)
|
||||
- Loaded in EW via [config.json](docs/config.md#modules)
|
||||
|
||||
# Triaging issues
|
||||
|
||||
Issues are triaged by community members and the Web App Team, following the [triage process](https://github.com/element-hq/element-meta/wiki/Triage-process).
|
||||
|
||||
@@ -272,20 +272,43 @@ Inheriting all the rules of TypeScript, the following additionally apply:
|
||||
18. Components should serve a single, or near-single, purpose.
|
||||
19. Prefer to derive information from component properties rather than establish state.
|
||||
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.
|
||||
|
||||
## 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 {
|
||||
@@ -296,7 +319,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,
|
||||
@@ -306,8 +329,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
|
||||
@@ -318,9 +340,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
|
||||
|
||||
|
||||
@@ -8,7 +8,13 @@ General description of the pattern can be found [here](https://en.wikipedia.org/
|
||||
|
||||
If you do MVVM right, your view should be dumb i.e it gets data from the view model and merely displays it.
|
||||
|
||||
### Practical guidelines for MVVM in element-web
|
||||
## Why are we using MVVM?
|
||||
|
||||
1. MVVM forces a separation of concern i.e we will no longer have large react components that have a lot of state and rendering code mixed together. This improves code readability and makes it easier to introduce changes.
|
||||
2. Introduces the possibility of code reuse. You can reuse an old view model with a new view or vice versa.
|
||||
3. Adding to the point above, in future you could import element-web view models to your project and supply your own views thus creating something similar to the [hydrogen sdk](https://github.com/element-hq/hydrogen-web/blob/master/doc/SDK.md).
|
||||
|
||||
## Practical guidelines for MVVM in element-web
|
||||
|
||||
A first documentation and implementation of MVVM was done in [MVVM-v1.md](MVVM-v1.md). This v1 version is now deprecated and this document describes the current implementation.
|
||||
|
||||
@@ -19,12 +25,12 @@ This is anywhere your data or business logic comes from. If your view model is a
|
||||
#### View
|
||||
|
||||
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`).
|
||||
3. Views use [useSyncExternalStore](https://react.dev/reference/react/useSyncExternalStore) internally where the view model is the external store.
|
||||
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 return type of your view model
|
||||
// Snapshot is the data that your view-model provides which is rendered by the view.
|
||||
interface FooViewSnapshot {
|
||||
value: string;
|
||||
}
|
||||
@@ -34,16 +40,16 @@ This is anywhere your data or business logic comes from. If your view model is a
|
||||
doSomething: () => void;
|
||||
}
|
||||
|
||||
// ViewModel is a type defining the methods needed for `useSyncExternalStore`
|
||||
// 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) {
|
||||
// useViewModel is a helper function that uses useSyncExternalStore under the hood
|
||||
const { value } = useViewModel(vm);
|
||||
return (
|
||||
<button type="button" onClick={() => vm.doSomething()}>
|
||||
@@ -82,8 +88,131 @@ This is anywhere your data or business logic comes from. If your view model is a
|
||||
|
||||
4. A full example is available [here](https://github.com/element-hq/element-web/blob/develop/src/viewmodels/audio/AudioPlayerViewModel.ts)
|
||||
|
||||
### Benefits
|
||||
### `useViewModel` hook
|
||||
|
||||
1. MVVM forces a separation of concern i.e we will no longer have large react components that have a lot of state and rendering code mixed together. This improves code readability and makes it easier to introduce changes.
|
||||
2. Introduces the possibility of code reuse. You can reuse an old view model with a new view or vice versa.
|
||||
3. Adding to the point above, in future you could import element-web view models to your project and supply your own views thus creating something similar to the [hydrogen sdk](https://github.com/element-hq/hydrogen-web/blob/master/doc/SDK.md).
|
||||
Your view must call this hook with the view-model as argument. Think of this as your view subscribing to the view model.<br>
|
||||
This hook returns the snapshot from your view-model.
|
||||
|
||||
## Disposables and helper hooks
|
||||
|
||||
Disposables provide a mechanism for tracking and releases resources. This is necessary for avoiding memory leaks.
|
||||
|
||||
### Lifecycle of a view model
|
||||
|
||||
The lifecycle of a given view model is from its creation (usually through the constructor i.e `new FooViewModel(prop1, prop2)`) to the time the `dispose` method on it is called (eg: `fooViewModel.dispose()`). It is the responsibility of whoever creates the view model to call the dispose method when the view model is no longer necessary.
|
||||
|
||||
Disposable work by tracking anything that needs to be disposed of and then sequentially disposing them when `viewModel.dispose()` is called.
|
||||
|
||||
### How to use disposables
|
||||
|
||||
Consider the following scenarios:
|
||||
|
||||
#### Scenario 1: Your view model listens to some event on an event emitter <br>
|
||||
|
||||
In the example given below, how do you ensure that the listener on `props.emitter` is removed when the view model is disposed?
|
||||
|
||||
```ts
|
||||
class FooViewModel ... {
|
||||
constructor(props: Props) {
|
||||
...
|
||||
props.emitter.on("my-event", this.doSomething());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can use disposables to remove the listener when the view-model is disposed:
|
||||
|
||||
```ts
|
||||
class FooViewModel ... {
|
||||
constructor(props: Props) {
|
||||
...
|
||||
this.disposables.trackListener(props.emitter, "my-event", this.doSomething());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Scenario 2: Your view model creates sub view models <br>
|
||||
|
||||
```ts
|
||||
class FooViewModel ... {
|
||||
constructor(props: Props) {
|
||||
...
|
||||
this.barViewModel = new BarViewModel(...);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here, we want to ensure that when `FooViewModel.dispose()` is called, it also disposes any sub view models (in this case `BarViewModel`):
|
||||
|
||||
```ts
|
||||
class FooViewModel ... {
|
||||
constructor(props: Props) {
|
||||
...
|
||||
this.barViewModel = this.disposables.track(new BarViewModel(...));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Scenario 3: Tracking and disposing arbitrary resources <br>
|
||||
|
||||
A disposable is:
|
||||
|
||||
- a function
|
||||
- an object with `dispose` method (like a view model)
|
||||
|
||||
You can therefore use disposables to track any resource that must be eventually relinquished, eg:
|
||||
|
||||
```ts
|
||||
class Call {
|
||||
....
|
||||
public endCall();
|
||||
public stopConnections();
|
||||
}
|
||||
|
||||
class CallViewModel {
|
||||
...
|
||||
constructor(props: Props) {
|
||||
const call = new Call();
|
||||
// When the view model is disposed, the following call methods will be called
|
||||
this.disposables.track(() => {
|
||||
call.endCall();
|
||||
call.stopConnections();
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Disposing view models from non-MVVMed react components
|
||||
|
||||
While we eventually want all our UI code to use MVVM, the current reality is that most of the existing code is just normal react components. We follow a bottoms up approach when it comes to moving code over to MVVM i.e we deal with child components before dealing with parent components.
|
||||
|
||||
This means that you need to dispose child view models from the non-MVVMed parent component.
|
||||
|
||||
#### Class component:
|
||||
|
||||
Create the view model in `componentDidMount()` and dispose the view model in `componentWillUnmount()`:
|
||||
|
||||
```ts
|
||||
class FooComponent extends Component {
|
||||
componentDidMount() {
|
||||
this.barViewModel = new BarViewModel(...);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.barViewModel.dispose();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Functional Component:
|
||||
|
||||
Use the `useCreateAutoDisposedViewModel` hook:
|
||||
|
||||
```ts
|
||||
export function FooComponent(props) {
|
||||
const vm = useCreateAutoDisposedViewModel(() => new BarViewModel(...));
|
||||
return <BarView vm={vm}>;
|
||||
}
|
||||
```
|
||||
|
||||
This hook will call the `dispose` method on the view model when `FooComponent` is unmounted.
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
# Build
|
||||
|
||||
- [Customisations](customisations.md)
|
||||
- [Modules](modules.md)
|
||||
- [Deprecated Modules](deprecated-modules.md)
|
||||
- [Native Node modules](native-node-modules.md)
|
||||
|
||||
# Contribution
|
||||
@@ -40,6 +40,8 @@
|
||||
- [Feature flags](feature-flags.md)
|
||||
- [OIDC and delegated authentication](oidc.md)
|
||||
- [Release Process](release.md)
|
||||
- [MVVM](MVVM.md)
|
||||
- [Settings](settings.md)
|
||||
|
||||
# Deep dive
|
||||
|
||||
|
||||
@@ -407,6 +407,7 @@ If you run your own rageshake server to collect bug reports, the following optio
|
||||
1. `bug_report_endpoint_url`: URL for where to submit rageshake logs to. Rageshakes include feedback submissions and bug reports. When
|
||||
not present in the config, the app will disable all rageshake functionality. Set to `https://rageshakes.element.io/api/submit` to submit
|
||||
rageshakes to us, or use your own rageshake server.
|
||||
You may also set the value to `"local"` if you wish to only store logs locally, in order to download them for debugging.
|
||||
2. `uisi_autorageshake_app`: If a user has enabled the "automatically send debug logs on decryption errors" flag, this option will be sent
|
||||
alongside the rageshake so the rageshake server can filter them by app name. By default, this will be `element-auto-uisi`
|
||||
(in contrast to other rageshakes submitted by the app, which use `element-web`).
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
# Module system
|
||||
# Deprecated Module system
|
||||
|
||||
> [!CAUTION]
|
||||
> 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
|
||||
@@ -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.
|
||||
|
||||
@@ -11,7 +11,7 @@ import { env } from "process";
|
||||
import type { Config } from "jest";
|
||||
|
||||
const config: Config = {
|
||||
testEnvironment: "jsdom",
|
||||
testEnvironment: "jest-fixed-jsdom",
|
||||
testEnvironmentOptions: {
|
||||
url: "http://localhost/",
|
||||
// This is needed to be able to load dual CJS/ESM WASM packages e.g. rust crypto & matrix-wywiwyg
|
||||
@@ -39,11 +39,11 @@ const config: Config = {
|
||||
"workers/(.+)Factory": "<rootDir>/__mocks__/workerFactoryMock.js",
|
||||
"^!!raw-loader!.*": "jest-raw-loader",
|
||||
"recorderWorkletFactory": "<rootDir>/__mocks__/empty.js",
|
||||
"^fetch-mock$": "<rootDir>/node_modules/fetch-mock",
|
||||
"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)).+$",
|
||||
"/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)).+$",
|
||||
],
|
||||
collectCoverageFrom: [
|
||||
"<rootDir>/src/**/*.{js,ts,tsx}",
|
||||
|
||||
@@ -1,28 +1,34 @@
|
||||
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/**/*",
|
||||
],
|
||||
},
|
||||
},
|
||||
ignoreDependencies: [
|
||||
// Required for `action-validator`
|
||||
"@action-validator/*",
|
||||
@@ -43,7 +49,11 @@ export default {
|
||||
// Embedded into webapp
|
||||
"@element-hq/element-call-embedded",
|
||||
// Transitive dep of jest
|
||||
"jsdom",
|
||||
"@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
|
||||
@@ -51,13 +61,6 @@ export default {
|
||||
// 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,
|
||||
} satisfies KnipConfig;
|
||||
|
||||
@@ -18,6 +18,18 @@
|
||||
"file": "element-web.json",
|
||||
"excludes": ["src/i18n/strings/en_EN.json"],
|
||||
"lang": "${autodetectLang}"
|
||||
},
|
||||
{
|
||||
"pattern": "packages/shared-components/src/i18n/strings/en_EN.json",
|
||||
"file": "shared-components.json",
|
||||
"lang": "inherited"
|
||||
},
|
||||
{
|
||||
"group": "existing",
|
||||
"pattern": "packages/shared-components/src/i18n/strings/*.json",
|
||||
"file": "shared-components.json",
|
||||
"excludes": ["packages/shared-components/src/i18n/strings/en_EN.json"],
|
||||
"lang": "${autodetectLang}"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -27,6 +39,10 @@
|
||||
{
|
||||
"conditions": "equals: ${file}, element-web.json",
|
||||
"output": "src/i18n/strings/${langLsrUnderscore}.json"
|
||||
},
|
||||
{
|
||||
"conditions": "equals: ${file}, shared-components.json",
|
||||
"output": "packages/shared-components/src/i18n/strings/${langLsrUnderscore}.json"
|
||||
}
|
||||
],
|
||||
"includeSourceLang": "${includeSourceLang|false}",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "element-web",
|
||||
"version": "1.12.8",
|
||||
"version": "1.12.10",
|
||||
"description": "Element: the future of secure communication",
|
||||
"author": "New Vector Ltd.",
|
||||
"repository": {
|
||||
@@ -29,9 +29,9 @@
|
||||
"UserFriendlyError"
|
||||
],
|
||||
"scripts": {
|
||||
"i18n": "matrix-gen-i18n src res packages/shared-components/src && yarn i18n:sort && yarn i18n:lint",
|
||||
"i18n:sort": "jq --sort-keys '.' src/i18n/strings/en_EN.json > src/i18n/strings/en_EN.json.tmp && mv src/i18n/strings/en_EN.json.tmp src/i18n/strings/en_EN.json",
|
||||
"i18n:lint": "matrix-i18n-lint && prettier --log-level=silent --write src/i18n/strings/ --ignore-path /dev/null",
|
||||
"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",
|
||||
@@ -69,15 +69,16 @@
|
||||
"postinstall": "patch-package"
|
||||
},
|
||||
"resolutions": {
|
||||
"**/pretty-format/react-is": "19.2.1",
|
||||
"@types/react": "19.2.7",
|
||||
"**/pretty-format/react-is": "19.2.3",
|
||||
"@types/react": "19.2.8",
|
||||
"@types/react-dom": "19.2.3",
|
||||
"oidc-client-ts": "3.4.1",
|
||||
"jwt-decode": "4.0.0",
|
||||
"caniuse-lite": "1.0.30001759",
|
||||
"caniuse-lite": "1.0.30001764",
|
||||
"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",
|
||||
@@ -85,20 +86,19 @@
|
||||
"@element-hq/web-shared-components": "link:packages/shared-components",
|
||||
"@fontsource/fira-code": "^5",
|
||||
"@fontsource/inter": "^5",
|
||||
"@formatjs/intl-segmenter": "^11.5.7",
|
||||
"@matrix-org/analytics-events": "^0.30.0",
|
||||
"@formatjs/intl-segmenter": "^12.0.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.4.3",
|
||||
"@vector-im/compound-web": "^8.3.4",
|
||||
"@vector-im/compound-design-tokens": "6.9.0",
|
||||
"@vector-im/compound-web": "^8.3.6",
|
||||
"@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",
|
||||
@@ -129,15 +129,15 @@
|
||||
"lodash": "^4.17.21",
|
||||
"maplibre-gl": "^5.0.0",
|
||||
"matrix-encrypt-attachment": "^1.0.3",
|
||||
"matrix-js-sdk": "40.0.0",
|
||||
"matrix-widget-api": "^1.15.0",
|
||||
"matrix-js-sdk": "40.2.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.302.2",
|
||||
"posthog-js": "1.328.0",
|
||||
"qrcode": "1.5.4",
|
||||
"re-resizable": "6.11.2",
|
||||
"react": "^19.0.0",
|
||||
@@ -145,15 +145,12 @@
|
||||
"react-blurhash": "^0.3.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"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,12 +178,12 @@
|
||||
"@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.0.0",
|
||||
"@element-hq/element-web-playwright-common": "2.2.5",
|
||||
"@fetch-mock/jest": "^0.2.20",
|
||||
"@peculiar/webcrypto": "^1.4.3",
|
||||
"@playwright/test": "1.57.0",
|
||||
"@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",
|
||||
@@ -210,10 +207,9 @@
|
||||
"@types/minimist": "^1.2.5",
|
||||
"@types/modernizr": "^3.5.3",
|
||||
"@types/node": "18",
|
||||
"@types/node-fetch": "^2.6.2",
|
||||
"@types/pako": "^2.0.0",
|
||||
"@types/qrcode": "^1.3.5",
|
||||
"@types/react": "19.2.7",
|
||||
"@types/react": "19.2.8",
|
||||
"@types/react-beautiful-dnd": "^13.0.0",
|
||||
"@types/react-dom": "19.2.3",
|
||||
"@types/react-transition-group": "^4.4.0",
|
||||
@@ -231,7 +227,6 @@
|
||||
"chokidar": "^5.0.0",
|
||||
"concurrently": "^9.0.0",
|
||||
"copy-webpack-plugin": "^13.0.0",
|
||||
"core-js": "^3.38.1",
|
||||
"cronstrue": "^3.0.0",
|
||||
"css-loader": "^7.0.0",
|
||||
"css-minimizer-webpack-plugin": "^7.0.0",
|
||||
@@ -250,27 +245,24 @@
|
||||
"eslint-plugin-unicorn": "^56.0.0",
|
||||
"express": "^5.0.0",
|
||||
"fake-indexeddb": "^6.0.0",
|
||||
"fetch-mock": "9.11.0",
|
||||
"fetch-mock-jest": "^1.5.1",
|
||||
"file-loader": "^6.0.0",
|
||||
"html-webpack-plugin": "^5.5.3",
|
||||
"husky": "^9.0.0",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"jest": "^30.0.0",
|
||||
"jest-canvas-mock": "^2.5.2",
|
||||
"jest-environment-jsdom": "^30.0.0",
|
||||
"jest-environment-jsdom": "^30.2.0",
|
||||
"jest-fixed-jsdom": "^0.0.11",
|
||||
"jest-mock": "^30.0.0",
|
||||
"jest-raw-loader": "^1.0.1",
|
||||
"jsqr": "^1.4.0",
|
||||
"knip": "^5.36.2",
|
||||
"lint-staged": "^16.0.0",
|
||||
"matrix-web-i18n": "^3.2.1",
|
||||
"matrix-web-i18n": "3.6.0",
|
||||
"mini-css-extract-plugin": "2.9.2",
|
||||
"minimist": "^1.2.6",
|
||||
"modernizr": "^3.12.0",
|
||||
"node-fetch": "^2.6.7",
|
||||
"patch-package": "^8.0.0",
|
||||
"playwright-core": "^1.51.0",
|
||||
"postcss": "8.4.46",
|
||||
"postcss-easings": "^4.0.0",
|
||||
"postcss-hexrgba": "2.1.0",
|
||||
@@ -278,23 +270,22 @@
|
||||
"postcss-loader": "8.1.1",
|
||||
"postcss-mixins": "^12.0.0",
|
||||
"postcss-nested": "^7.0.0",
|
||||
"postcss-preset-env": "^10.0.0",
|
||||
"postcss-preset-env": "^11.0.0",
|
||||
"postcss-scss": "^4.0.4",
|
||||
"postcss-simple-vars": "^7.0.1",
|
||||
"prettier": "3.7.4",
|
||||
"prettier": "3.8.0",
|
||||
"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.8.3",
|
||||
"typescript": "5.9.3",
|
||||
"util": "^0.12.5",
|
||||
"web-streams-polyfill": "^4.0.0",
|
||||
"webpack": "^5.89.0",
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# Ignore test failure screenshots
|
||||
/src/**/__screenshots__/
|
||||
|
||||
# Ignore vis diffs & local baseline
|
||||
/__vis__/**/__diffs__
|
||||
/__vis__/**/__results__
|
||||
/__vis__/local
|
||||
|
||||
# Ignore coverage report
|
||||
/coverage/
|
||||
@@ -0,0 +1,5 @@
|
||||
// Even though this (at time of writing) is identical Element Web's
|
||||
// .prettierrc.js, shared components needs its own because otherwise
|
||||
// this refers to element web's copy of eslint-plugin-matrix-org which
|
||||
// would require element-web's modules to be installed.
|
||||
module.exports = require("eslint-plugin-matrix-org/.prettierrc.js");
|
||||
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* 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 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,16 +10,14 @@ import { WithTooltip, IconButton, TooltipLinkList } from "storybook/internal/com
|
||||
import React from "react";
|
||||
import { GlobeIcon } from "@storybook/icons";
|
||||
|
||||
// We can't import `shared/i18n.tsx` directly here.
|
||||
// The storybook addon doesn't seem to benefit the vite config of storybook and we can't resolve the alias in i18n.tsx.
|
||||
import json from "../../../webapp/i18n/languages.json";
|
||||
const languages = Object.keys(json).filter((lang) => lang !== "default");
|
||||
const languages: string[] = JSON.parse(process.env.STORYBOOK_LANGUAGES!);
|
||||
|
||||
/**
|
||||
* Returns the title of a language in the user's locale.
|
||||
*/
|
||||
function languageTitle(language: string): string {
|
||||
return new Intl.DisplayNames([language], { type: "language", style: "short" }).of(language) || language;
|
||||
const normalisedLang = language.toLowerCase().replace("_", "-");
|
||||
return new Intl.DisplayNames([normalisedLang], { type: "language", style: "short" }).of(normalisedLang) || language;
|
||||
}
|
||||
|
||||
export const languageAddon: Addon = {
|
||||
|
||||
@@ -6,14 +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 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)"],
|
||||
staticDirs: ["../../../webapp"],
|
||||
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,
|
||||
@@ -23,14 +52,31 @@ 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: ["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
|
||||
res.setHeader("Content-Type", "application/json");
|
||||
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
|
||||
// the app requests.
|
||||
const langFile = req.url.split("/").pop();
|
||||
res.setHeader("Content-Type", "application/json");
|
||||
fs.createReadStream(`src/i18n/strings/${langFile}`).pipe(res);
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
// Needed for counterpart to work
|
||||
plugins: [nodePolyfills({ include: ["process", "util"] })],
|
||||
],
|
||||
server: {
|
||||
allowedHosts: ["localhost", ".docker.internal"],
|
||||
},
|
||||
@@ -42,5 +88,9 @@ const config: StorybookConfig = {
|
||||
url: "https://element-hq.github.io/compound-web/",
|
||||
},
|
||||
},
|
||||
env: (config) => ({
|
||||
...config,
|
||||
STORYBOOK_LANGUAGES: JSON.stringify(Object.keys(languages)),
|
||||
}),
|
||||
};
|
||||
export default config;
|
||||
|
||||
@@ -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();
|
||||
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);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,303 @@
|
||||
# @element-hq/web-shared-components
|
||||
|
||||
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
|
||||
Web](https://github.com/element-hq/compound-web). This is not a design system
|
||||
by itself, but rather a set of larger components.
|
||||
|
||||
## Installation in a new project
|
||||
|
||||
When adding this library to a new project, as well as installing
|
||||
`@element-hq/web-shared-components` as normal, you will also need to add
|
||||
[compound-web](https://github.com/element-hq/compound-web) as a peer
|
||||
dependency:
|
||||
|
||||
```bash
|
||||
yarn add @element-hq/web-shared-components
|
||||
yarn add @vector-im/compound-web
|
||||
```
|
||||
|
||||
(This avoids problems where we end up with different versions of compound-web in the
|
||||
top-level project and web-shared-components).
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Import
|
||||
|
||||
Both JavaScript and CSS can be imported as follows:
|
||||
|
||||
```javascript
|
||||
import { RoomListHeaderView, useViewModel } from "@element-hq/web-shared-components";
|
||||
import "@element-hq/web-shared-components/dist/element-web-shared-components.css";
|
||||
```
|
||||
|
||||
or in CSS file:
|
||||
|
||||
```css
|
||||
@import url("@element-hq/web-shared-components");
|
||||
```
|
||||
|
||||
### Using Components
|
||||
|
||||
There are two kinds of components in this library:
|
||||
|
||||
- _regular_ react component which doesn't follow specific pattern.
|
||||
- _view_ component(MVVM pattern).
|
||||
|
||||
> [!TIP]
|
||||
> These components are available in the project storybook.
|
||||
|
||||
#### Regular Components
|
||||
|
||||
These components can be used directly by passing props. Example:
|
||||
|
||||
```tsx
|
||||
import { Flex } from "@element-hq/web-shared-components";
|
||||
function MyApp() {
|
||||
return <Flex align="center" />;
|
||||
}
|
||||
```
|
||||
|
||||
#### View (MVVM) Components
|
||||
|
||||
These components follow the [MVVM pattern](../../docs/MVVM.md). A ViewModel
|
||||
instance should be provided as a prop.
|
||||
|
||||
Here's a basic example:
|
||||
|
||||
```jsx
|
||||
import { ViewExample } from "@element-hq/web-shared-components";
|
||||
|
||||
function MyApp() {
|
||||
const viewModel = new ViewModelExample();
|
||||
return <ViewExample vm={viewModel} />;
|
||||
}
|
||||
```
|
||||
|
||||
### Utilities
|
||||
|
||||
#### Internationalization
|
||||
|
||||
- `useI18n()` - Hook for translations
|
||||
- `I18nApi` - Internationalization API utilities
|
||||
|
||||
#### Date & Time
|
||||
|
||||
- `DateUtils` - Date formatting and manipulation
|
||||
- `humanize` - Human-readable time formatting
|
||||
|
||||
#### Formatting
|
||||
|
||||
- `FormattingUtils` - Text and data formatting utilities
|
||||
- `numbers` - Number formatting utilities
|
||||
|
||||
## Development
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Node.js >= 20.0.0
|
||||
- Yarn 1.22.22+
|
||||
|
||||
### Setup
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
yarn install
|
||||
|
||||
# Build the library
|
||||
yarn prepare
|
||||
```
|
||||
|
||||
### Running Storybook
|
||||
|
||||
```bash
|
||||
yarn storybook
|
||||
```
|
||||
|
||||
### Write components
|
||||
|
||||
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.
|
||||
|
||||
### 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 Vitest
|
||||
and React Testing Library.
|
||||
|
||||
```bash
|
||||
yarn test:unit
|
||||
```
|
||||
|
||||
### Visual Regression Tests
|
||||
|
||||
These tests ensure the UI components render correctly.
|
||||
Built with Storybook and run under vitest using playwright.
|
||||
|
||||
```bash
|
||||
yarn 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).
|
||||
To generate translation strings for this package, run:
|
||||
|
||||
```bash
|
||||
yarn 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: 8.1 KiB |
|
After Width: | Height: | Size: 9.9 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 9.5 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 7.1 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 8.6 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 7.0 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 14 KiB |