mirror of
https://github.com/element-hq/element-web.git
synced 2025-12-09 01:30:57 +00:00
Compare commits
243 Commits
renovate/t
...
renovate/p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
38863a2546 | ||
|
|
54cbbb6ff0 | ||
|
|
69abef1f1c | ||
|
|
fd152c9c7e | ||
|
|
16e71ffd58 | ||
|
|
3a020c942a | ||
|
|
803c40c03c | ||
|
|
eeb2c0f690 | ||
|
|
32cce82790 | ||
|
|
5f07fbbc1b | ||
|
|
8753a9136f | ||
|
|
b33b1f3d01 | ||
|
|
08a1525937 | ||
|
|
135db543e1 | ||
|
|
b788e772d4 | ||
|
|
e8cf24abce | ||
|
|
4a62e87205 | ||
|
|
888994be45 | ||
|
|
e7eeb98c9c | ||
|
|
c4f22cc3b3 | ||
|
|
c11db3dd73 | ||
|
|
c45d8c599c | ||
|
|
75fa94084d | ||
|
|
661b026cd9 | ||
|
|
30bd70dacf | ||
|
|
20dbcd17fa | ||
|
|
716da7784a | ||
|
|
ad9150df2d | ||
|
|
5929269e31 | ||
|
|
4953a73ee0 | ||
|
|
0d7d2fb0a4 | ||
|
|
29a54405d8 | ||
|
|
4392aa1ed0 | ||
|
|
a721c5f4ea | ||
|
|
79f1176b92 | ||
|
|
92bb15fbba | ||
|
|
7aa7793640 | ||
|
|
f282be05ca | ||
|
|
744922cbcc | ||
|
|
7183d91930 | ||
|
|
cdedcc0b5a | ||
|
|
b679693702 | ||
|
|
fbb43d5e61 | ||
|
|
a79f6e7aa5 | ||
|
|
81c375007e | ||
|
|
aee24be1b4 | ||
|
|
1285b73be6 | ||
|
|
c203f02731 | ||
|
|
64130a018b | ||
|
|
e2fc1574bf | ||
|
|
de0492b786 | ||
|
|
0a46edaaff | ||
|
|
dd89cee328 | ||
|
|
29ff8a6199 | ||
|
|
184e6e3f29 | ||
|
|
7a01cdae0a | ||
|
|
06656a6472 | ||
|
|
5de9d5d24f | ||
|
|
0eff1caab2 | ||
|
|
b7acbe65c1 | ||
|
|
5736635a65 | ||
|
|
fcd23b48e0 | ||
|
|
250d6571fe | ||
|
|
f3a880f1c3 | ||
|
|
3d683ec5c6 | ||
|
|
81f1841aea | ||
|
|
e62125e61f | ||
|
|
c675453d72 | ||
|
|
ac0a91be9e | ||
|
|
333bec33ee | ||
|
|
f400d8db0a | ||
|
|
bb9c9982ef | ||
|
|
e2fd873f5e | ||
|
|
ac255445d1 | ||
|
|
425bc64aa9 | ||
|
|
9c6aa6942c | ||
|
|
5d66f9bd1a | ||
|
|
b894f8d65f | ||
|
|
6a1f0a7d22 | ||
|
|
bb582fa8f3 | ||
|
|
11b2ecb041 | ||
|
|
2ce59df1fe | ||
|
|
d85e5fca8d | ||
|
|
219a390025 | ||
|
|
2464178164 | ||
|
|
f96bfe9e18 | ||
|
|
ce529be5f4 | ||
|
|
c2c873520c | ||
|
|
36557d7383 | ||
|
|
03da342a4e | ||
|
|
caf7451862 | ||
|
|
5d17207a32 | ||
|
|
0cd108a3b4 | ||
|
|
4a9d065260 | ||
|
|
842edc6577 | ||
|
|
faadcf902e | ||
|
|
e883b05206 | ||
|
|
191f951303 | ||
|
|
b50fbe2eea | ||
|
|
8608268bc7 | ||
|
|
f6e85a38d2 | ||
|
|
39a5cca737 | ||
|
|
f751f2a55d | ||
|
|
f1bb017be7 | ||
|
|
f5e56cc8d5 | ||
|
|
3848d570be | ||
|
|
eade32a80c | ||
|
|
a0de60a045 | ||
|
|
a88a3575d5 | ||
|
|
abb93545fe | ||
|
|
24aa759544 | ||
|
|
77aa26bcdb | ||
|
|
4d66a85e73 | ||
|
|
52eb8a9979 | ||
|
|
c4ef57b5f1 | ||
|
|
d9e3aa52e2 | ||
|
|
b8e7c725e2 | ||
|
|
2617a7c3a5 | ||
|
|
42f8247c2e | ||
|
|
514dd07a28 | ||
|
|
53dc281a24 | ||
|
|
efc5b0260b | ||
|
|
d4c22d43f3 | ||
|
|
926bb56723 | ||
|
|
3dca2da5d6 | ||
|
|
71604c9e19 | ||
|
|
4e57b80556 | ||
|
|
c14d072cb7 | ||
|
|
dcf3e536ab | ||
|
|
18a5565b70 | ||
|
|
b0cdbf5eff | ||
|
|
486d4d59bc | ||
|
|
54f967efd5 | ||
|
|
24fc018845 | ||
|
|
36ccc1ae9a | ||
|
|
017aee9a8f | ||
|
|
e0a94a05ea | ||
|
|
23f372ca08 | ||
|
|
73fa27887d | ||
|
|
a9bb046e52 | ||
|
|
43485594b5 | ||
|
|
fdf54dd9c2 | ||
|
|
c7f07f4c29 | ||
|
|
bbb16f7ea9 | ||
|
|
3fd102602e | ||
|
|
af150b94ce | ||
|
|
299d7baf8b | ||
|
|
f297282bf6 | ||
|
|
3ae91d69da | ||
|
|
1dfd92c1eb | ||
|
|
1ec692c1e9 | ||
|
|
d558fa79e0 | ||
|
|
2ab42df0c8 | ||
|
|
a9993aef85 | ||
|
|
9830a8bc6e | ||
|
|
fa3c3b4c8c | ||
|
|
8f7ded8747 | ||
|
|
68ead7c989 | ||
|
|
ae2acdf311 | ||
|
|
209dfece21 | ||
|
|
f7e1a7b90e | ||
|
|
94ab980dc4 | ||
|
|
c408e12437 | ||
|
|
26b14dc2f8 | ||
|
|
542eb9d1ec | ||
|
|
5bcbf4f370 | ||
|
|
a5e5125907 | ||
|
|
e4e33cf239 | ||
|
|
3175377cb2 | ||
|
|
1c2b35d809 | ||
|
|
80a7de4314 | ||
|
|
3e809cd661 | ||
|
|
f8ec6b6f9b | ||
|
|
b7db85146f | ||
|
|
68e6b0f845 | ||
|
|
5888dfd29d | ||
|
|
d426063a22 | ||
|
|
09c3afd311 | ||
|
|
f6731ec318 | ||
|
|
7e04998a58 | ||
|
|
dae5b6b43b | ||
|
|
8376e43a03 | ||
|
|
e528fefd4c | ||
|
|
5e87f20cd8 | ||
|
|
7d9981e5dd | ||
|
|
1d20a4c5ee | ||
|
|
bc97560532 | ||
|
|
a6316d99d5 | ||
|
|
bea3574b30 | ||
|
|
0e6bacffed | ||
|
|
9be323dfd0 | ||
|
|
d6e50598cd | ||
|
|
41e33a0755 | ||
|
|
55a7bc5b13 | ||
|
|
b1fdf03236 | ||
|
|
39464f64ab | ||
|
|
09598bcb7a | ||
|
|
6e7dc4cc06 | ||
|
|
87fd279079 | ||
|
|
77c41d6789 | ||
|
|
9e1d0367d0 | ||
|
|
8fd6e29f2d | ||
|
|
cbcceee970 | ||
|
|
d0a8879971 | ||
|
|
e6e6f87d01 | ||
|
|
cf51b256ce | ||
|
|
9d973c88f9 | ||
|
|
e33894bed4 | ||
|
|
0468876aa0 | ||
|
|
c8f1c19517 | ||
|
|
2598e4ea22 | ||
|
|
ac96ab0d46 | ||
|
|
949d0dc8a9 | ||
|
|
0e0e928040 | ||
|
|
c519438dad | ||
|
|
146e4772ac | ||
|
|
6cfe197a38 | ||
|
|
a4a44a0c1c | ||
|
|
2ce2218549 | ||
|
|
a7ddf1c88a | ||
|
|
1b0863483a | ||
|
|
8bf2049f8a | ||
|
|
abafde4b64 | ||
|
|
756e1769ba | ||
|
|
1e28cd2b49 | ||
|
|
252546f085 | ||
|
|
a31dcaf157 | ||
|
|
735d2bd981 | ||
|
|
ccd7f923d0 | ||
|
|
8f3c1ce05e | ||
|
|
124afe3bf3 | ||
|
|
02246020a2 | ||
|
|
b0ee3c11ff | ||
|
|
b7dd05bc97 | ||
|
|
9c37cdf550 | ||
|
|
56f6c1ef46 | ||
|
|
b4396f5943 | ||
|
|
53ce37de9e | ||
|
|
b3188b47be | ||
|
|
5dc8edcae0 | ||
|
|
0d1da4ff45 | ||
|
|
c4d6a28473 | ||
|
|
6838969792 |
11
.eslintrc.js
11
.eslintrc.js
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
Copyright 2025 Element Creations Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
plugins: ["matrix-org", "eslint-plugin-react-compiler"],
|
||||
extends: ["plugin:matrix-org/babel", "plugin:matrix-org/react", "plugin:matrix-org/a11y"],
|
||||
@@ -160,6 +167,10 @@ module.exports = {
|
||||
group: ["@vector-im/compound-design-tokens/icons/*"],
|
||||
message: "Please use @vector-im/compound-design-tokens/assets/web/icons/* instead",
|
||||
},
|
||||
{
|
||||
group: ["**/packages/shared-components/**", "../packages/shared-components/**"],
|
||||
message: "Please use @element-hq/web-shared-components",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
6
.github/workflows/build.yml
vendored
6
.github/workflows/build.yml
vendored
@@ -42,9 +42,9 @@ jobs:
|
||||
run:
|
||||
shell: bash
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
|
||||
|
||||
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
|
||||
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # 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@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
|
||||
with:
|
||||
name: webapp-${{ matrix.image }}
|
||||
path: webapp
|
||||
|
||||
4
.github/workflows/build_debian.yaml
vendored
4
.github/workflows/build_debian.yaml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
R2_URL: ${{ vars.CF_R2_S3_API }}
|
||||
VERSION: ${{ github.ref_name }}
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
|
||||
|
||||
- name: Download package
|
||||
run: |
|
||||
@@ -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@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
|
||||
with:
|
||||
name: element-web.deb
|
||||
path: element-web.deb
|
||||
|
||||
6
.github/workflows/build_develop.yml
vendored
6
.github/workflows/build_develop.yml
vendored
@@ -26,9 +26,9 @@ jobs:
|
||||
R2_URL: ${{ vars.CF_R2_S3_API }}
|
||||
R2_PUBLIC_URL: "https://element-web-develop.element.io"
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
|
||||
|
||||
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
|
||||
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # 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@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
|
||||
with:
|
||||
name: webapp
|
||||
path: dist/develop.tar.gz
|
||||
|
||||
2
.github/workflows/deploy.yml
vendored
2
.github/workflows/deploy.yml
vendored
@@ -34,7 +34,7 @@ jobs:
|
||||
env:
|
||||
SITE: ${{ inputs.site || 'staging.element.io' }}
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
|
||||
|
||||
- name: Load GPG key
|
||||
run: |
|
||||
|
||||
10
.github/workflows/docker.yaml
vendored
10
.github/workflows/docker.yaml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
env:
|
||||
TEST_TAG: vectorim/element-web:test
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
|
||||
with:
|
||||
fetch-depth: 0 # needed for docker-package to be able to calculate the version
|
||||
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
if: github.event_name != 'pull_request'
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3
|
||||
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3
|
||||
@@ -96,7 +96,7 @@ jobs:
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5
|
||||
uses: docker/metadata-action@318604b99e75e41977312d83839a89be02ca4893 # v5
|
||||
if: github.event_name != 'pull_request'
|
||||
with:
|
||||
images: |
|
||||
@@ -132,7 +132,7 @@ jobs:
|
||||
cosign sign --yes ${images}
|
||||
|
||||
- name: Update repo description
|
||||
uses: peter-evans/dockerhub-description@432a30c9e07499fd01da9f8a49f0faf9e0ca5b77 # v4
|
||||
uses: peter-evans/dockerhub-description@1b9a80c056b620d92cedb9d9b5a223409c68ddfa # v5
|
||||
if: github.event_name != 'pull_request'
|
||||
continue-on-error: true
|
||||
with:
|
||||
@@ -141,7 +141,7 @@ jobs:
|
||||
repository: vectorim/element-web
|
||||
|
||||
- name: Repository Dispatch
|
||||
uses: peter-evans/repository-dispatch@ff45666b9427631e3450c54a1bcbee4d9ff4d7c0 # v3
|
||||
uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4
|
||||
if: github.event_name != 'pull_request'
|
||||
with:
|
||||
repository: element-hq/element-web-pro
|
||||
|
||||
10
.github/workflows/docs.yml
vendored
10
.github/workflows/docs.yml
vendored
@@ -17,23 +17,23 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Fetch element-desktop
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
|
||||
with:
|
||||
repository: element-hq/element-desktop
|
||||
path: element-desktop
|
||||
|
||||
- name: Fetch element-web
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
|
||||
with:
|
||||
path: element-web
|
||||
|
||||
- name: Fetch matrix-js-sdk
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
|
||||
with:
|
||||
repository: matrix-org/matrix-js-sdk
|
||||
path: matrix-js-sdk
|
||||
|
||||
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
|
||||
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6
|
||||
with:
|
||||
cache: "yarn"
|
||||
cache-dependency-path: element-web/yarn.lock
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
working-directory: element-web
|
||||
run: |
|
||||
yarn install --frozen-lockfile
|
||||
yarn ts-node ./scripts/gen-workflow-mermaid.ts ../element-desktop ../element-web ../matrix-js-sdk > docs/automations.md
|
||||
yarn node ./scripts/gen-workflow-mermaid.ts ../element-desktop ../element-web ../matrix-js-sdk > docs/automations.md
|
||||
echo "- [Automations](automations.md)" >> docs/SUMMARY.md
|
||||
|
||||
- name: Setup mdBook
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
actions: read
|
||||
steps:
|
||||
- name: Download HTML report
|
||||
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5
|
||||
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
run-id: ${{ github.event.workflow_run.id }}
|
||||
|
||||
22
.github/workflows/end-to-end-tests.yaml
vendored
22
.github/workflows/end-to-end-tests.yaml
vendored
@@ -50,11 +50,11 @@ jobs:
|
||||
runners-matrix: ${{ steps.runner-vars.outputs.matrix }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
|
||||
with:
|
||||
repository: element-hq/element-web
|
||||
|
||||
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
|
||||
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # 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@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
|
||||
with:
|
||||
name: webapp
|
||||
path: webapp
|
||||
@@ -122,18 +122,18 @@ jobs:
|
||||
- runAllTests: false
|
||||
project: Pinecone
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
repository: element-hq/element-web
|
||||
|
||||
- name: 📥 Download artifact
|
||||
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5
|
||||
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
|
||||
with:
|
||||
name: webapp
|
||||
path: webapp
|
||||
|
||||
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
|
||||
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6
|
||||
with:
|
||||
cache: "yarn"
|
||||
cache-dependency-path: yarn.lock
|
||||
@@ -172,7 +172,7 @@ jobs:
|
||||
|
||||
- name: Upload blob report to GitHub Actions Artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
|
||||
with:
|
||||
name: all-blob-reports-${{ matrix.project }}-${{ matrix.runner }}
|
||||
path: blob-report
|
||||
@@ -194,13 +194,13 @@ jobs:
|
||||
if: always()
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
|
||||
if: inputs.skip != true
|
||||
with:
|
||||
persist-credentials: false
|
||||
repository: element-hq/element-web
|
||||
|
||||
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
|
||||
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # 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@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5
|
||||
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
|
||||
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@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
|
||||
with:
|
||||
name: html-report
|
||||
path: playwright-report
|
||||
|
||||
2
.github/workflows/netlify.yaml
vendored
2
.github/workflows/netlify.yaml
vendored
@@ -28,7 +28,7 @@ jobs:
|
||||
Exercise caution. Use test accounts.
|
||||
|
||||
- name: 📥 Download artifact
|
||||
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5
|
||||
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
run-id: ${{ github.event.workflow_run.id }}
|
||||
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
|
||||
|
||||
- name: Update synapse image
|
||||
run: |
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
|
||||
- name: Create Pull Request
|
||||
id: cpr
|
||||
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7
|
||||
uses: peter-evans/create-pull-request@84ae59a2cdc2258d6fa0732dd66352dddae2a412 # v7
|
||||
with:
|
||||
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
branch: actions/playwright-image-updates
|
||||
|
||||
6
.github/workflows/release_prepare.yml
vendored
6
.github/workflows/release_prepare.yml
vendored
@@ -41,7 +41,7 @@ jobs:
|
||||
REPOS: matrix-js-sdk element-web element-desktop
|
||||
steps:
|
||||
- name: Checkout Element Desktop
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
|
||||
if: inputs.element-desktop
|
||||
with:
|
||||
repository: element-hq/element-desktop
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
fetch-tags: true
|
||||
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
- name: Checkout Element Web
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
|
||||
if: inputs.element-web
|
||||
with:
|
||||
repository: element-hq/element-web
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
fetch-tags: true
|
||||
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
- name: Checkout Matrix JS SDK
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
|
||||
if: inputs.matrix-js-sdk
|
||||
with:
|
||||
repository: matrix-org/matrix-js-sdk
|
||||
|
||||
40
.github/workflows/shared-component-publish.yaml
vendored
Normal file
40
.github/workflows/shared-component-publish.yaml
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
name: Publish shared component npm package
|
||||
on:
|
||||
workflow_dispatch: {}
|
||||
|
||||
concurrency: release
|
||||
jobs:
|
||||
publish:
|
||||
name: "Publish"
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: 🧮 Checkout code
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
|
||||
|
||||
- name: 🔧 Set up node environment
|
||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6
|
||||
with:
|
||||
cache: "yarn"
|
||||
node-version-file: ".node-version"
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
|
||||
# Ensure npm 11.5.1 or later is installed
|
||||
- name: Update npm
|
||||
run: npm install -g npm@latest
|
||||
|
||||
# Need to setup element web too as it needs the translations
|
||||
- name: 🛠️ Setup EW
|
||||
run: yarn install --pure-lockfile
|
||||
|
||||
- name: 🛠️ Setup
|
||||
# When running `install` it also calls the `prepare` step which generates
|
||||
# a build
|
||||
run: yarn --cwd packages/shared-components install --pure-lockfile
|
||||
|
||||
- name: 🚀 Publish to npm
|
||||
working-directory: packages/shared-components
|
||||
run: npm publish --access public --tag test --provenance
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
run: "sudo apt-get install -y tree"
|
||||
|
||||
- name: Download Diffs
|
||||
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5
|
||||
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
run-id: ${{ github.event.workflow_run.id }}
|
||||
|
||||
@@ -21,12 +21,12 @@ jobs:
|
||||
issues: read
|
||||
pull-requests: read
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
repository: element-hq/element-web
|
||||
|
||||
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
|
||||
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6
|
||||
with:
|
||||
cache: "yarn"
|
||||
node-version: "lts/*"
|
||||
@@ -55,22 +55,12 @@ jobs:
|
||||
if: steps.playwright-cache.outputs.cache-hit != 'true'
|
||||
run: "yarn playwright install --with-deps --only-shell"
|
||||
|
||||
- name: Build Element Web resources
|
||||
# Needed to prepare language files
|
||||
run: "yarn build:res"
|
||||
|
||||
- name: Build storybook dependencies
|
||||
# When the first test is ran, it will fail because the dependencies are not yet built.
|
||||
# This step is to ensure that the dependencies are built before running the tests.
|
||||
run: "yarn --cwd packages/shared-components test:storybook:ci"
|
||||
continue-on-error: true
|
||||
|
||||
- name: Run Visual tests
|
||||
run: "yarn --cwd packages/shared-components test:storybook:ci"
|
||||
|
||||
- name: Upload received images & diffs
|
||||
if: always()
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
|
||||
with:
|
||||
name: received-images
|
||||
path: packages/shared-components/playwright/shared-component-received
|
||||
|
||||
22
.github/workflows/static_analysis.yaml
vendored
22
.github/workflows/static_analysis.yaml
vendored
@@ -22,9 +22,9 @@ jobs:
|
||||
name: "Typescript Syntax Check"
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
|
||||
|
||||
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
|
||||
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6
|
||||
with:
|
||||
cache: "yarn"
|
||||
node-version: "lts/*"
|
||||
@@ -63,7 +63,7 @@ jobs:
|
||||
name: "Rethemendex Check"
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
|
||||
|
||||
- run: ./res/css/rethemendex.sh
|
||||
|
||||
@@ -73,9 +73,9 @@ jobs:
|
||||
name: "ESLint"
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
|
||||
|
||||
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
|
||||
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6
|
||||
with:
|
||||
cache: "yarn"
|
||||
node-version: "lts/*"
|
||||
@@ -97,9 +97,9 @@ jobs:
|
||||
name: "Style Lint"
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
|
||||
|
||||
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
|
||||
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6
|
||||
with:
|
||||
cache: "yarn"
|
||||
node-version: "lts/*"
|
||||
@@ -115,9 +115,9 @@ jobs:
|
||||
name: "Workflow Lint"
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
|
||||
|
||||
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
|
||||
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6
|
||||
with:
|
||||
cache: "yarn"
|
||||
node-version: "lts/*"
|
||||
@@ -133,9 +133,9 @@ jobs:
|
||||
name: "Analyse Dead Code"
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
|
||||
|
||||
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
|
||||
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6
|
||||
with:
|
||||
cache: "yarn"
|
||||
node-version: "lts/*"
|
||||
|
||||
67
.github/workflows/tests.yml
vendored
67
.github/workflows/tests.yml
vendored
@@ -29,8 +29,8 @@ env:
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
jest:
|
||||
name: Jest
|
||||
jest_ew:
|
||||
name: Jest (Element Web)
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -39,12 +39,12 @@ jobs:
|
||||
runner: [1, 2]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
|
||||
with:
|
||||
repository: ${{ inputs.matrix-js-sdk-sha && 'element-hq/element-web' || github.repository }}
|
||||
|
||||
- name: Yarn cache
|
||||
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
|
||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6
|
||||
with:
|
||||
node-version: "lts/*"
|
||||
cache: "yarn"
|
||||
@@ -84,7 +84,7 @@ jobs:
|
||||
|
||||
- name: Upload Artifact
|
||||
if: env.ENABLE_COVERAGE == 'true'
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
|
||||
with:
|
||||
name: coverage-${{ matrix.runner }}
|
||||
path: |
|
||||
@@ -93,13 +93,13 @@ jobs:
|
||||
|
||||
complete:
|
||||
name: jest-tests
|
||||
needs: jest
|
||||
needs: jest_ew
|
||||
if: always()
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
statuses: write
|
||||
steps:
|
||||
- if: needs.jest.result != 'skipped' && needs.jest.result != 'success'
|
||||
- if: needs.jest_ew.result != 'skipped' && needs.jest_ew.result != 'success'
|
||||
run: exit 1
|
||||
|
||||
- name: Skip SonarCloud in merge queue
|
||||
@@ -112,3 +112,56 @@ jobs:
|
||||
context: SonarCloud Code Analysis
|
||||
sha: ${{ github.sha }}
|
||||
target_url: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
|
||||
jest_sc:
|
||||
name: Jest (Shared Components)
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
|
||||
with:
|
||||
repository: ${{ inputs.matrix-js-sdk-sha && 'element-hq/element-web' || github.repository }}
|
||||
|
||||
- name: Yarn cache
|
||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # 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
|
||||
with:
|
||||
path: /tmp/jest_cache
|
||||
key: ${{ hashFiles('**/yarn.lock') }}
|
||||
|
||||
- name: Get number of CPU cores
|
||||
id: cpu-cores
|
||||
uses: SimenB/github-actions-cpu-cores@97ba232459a8e02ff6121db9362b09661c875ab8 # v2
|
||||
|
||||
- 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
|
||||
|
||||
- name: Upload Artifact
|
||||
if: env.ENABLE_COVERAGE == 'true'
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
|
||||
with:
|
||||
name: coverage-sharedcomponents
|
||||
path: |
|
||||
packages/shared-components/coverage
|
||||
!packages/shared-components/coverage/lcov-report
|
||||
|
||||
@@ -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@8ad880e4d437783ea2ab17010324de1075228110 # v2.3.2
|
||||
- uses: octokit/graphql-action@abaeca7ba4f0325d63b8de7ef943c2418d161b93 # v3.0.0
|
||||
id: find_team_members
|
||||
with:
|
||||
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
||||
@@ -52,7 +52,7 @@ jobs:
|
||||
fi
|
||||
env:
|
||||
TEAM: "design"
|
||||
- uses: octokit/graphql-action@8ad880e4d437783ea2ab17010324de1075228110 # v2.3.2
|
||||
- uses: octokit/graphql-action@abaeca7ba4f0325d63b8de7ef943c2418d161b93 # v3.0.0
|
||||
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@8ad880e4d437783ea2ab17010324de1075228110 # v2.3.2
|
||||
- uses: octokit/graphql-action@abaeca7ba4f0325d63b8de7ef943c2418d161b93 # v3.0.0
|
||||
id: find_team_members
|
||||
with:
|
||||
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
||||
@@ -119,7 +119,7 @@ jobs:
|
||||
fi
|
||||
env:
|
||||
TEAM: "product"
|
||||
- uses: octokit/graphql-action@8ad880e4d437783ea2ab17010324de1075228110 # v2.3.2
|
||||
- uses: octokit/graphql-action@abaeca7ba4f0325d63b8de7ef943c2418d161b93 # v3.0.0
|
||||
id: add_to_project
|
||||
if: steps.any_matching_reviewers.outputs.match == 'true'
|
||||
with:
|
||||
|
||||
2
.github/workflows/triage-stale.yml
vendored
2
.github/workflows/triage-stale.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/stale@3a9db7e6a41a89f618792c92c0e97cc736e1b13f # v10
|
||||
- uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10
|
||||
with:
|
||||
operations-per-run: 100
|
||||
|
||||
|
||||
6
.github/workflows/update-jitsi.yml
vendored
6
.github/workflows/update-jitsi.yml
vendored
@@ -9,9 +9,9 @@ jobs:
|
||||
update:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
|
||||
|
||||
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
|
||||
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # 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@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7
|
||||
uses: peter-evans/create-pull-request@84ae59a2cdc2258d6fa0732dd66352dddae2a412 # v7
|
||||
with:
|
||||
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
branch: actions/jitsi-update
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -36,3 +36,4 @@ storybook-static
|
||||
|
||||
/packages/shared-components/node_modules
|
||||
/packages/shared-components/dist
|
||||
/packages/shared-components/src/i18nKeys.d.ts
|
||||
|
||||
@@ -1 +1 @@
|
||||
22
|
||||
24
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
/dist
|
||||
/lib
|
||||
/node_modules
|
||||
/packages/
|
||||
/webapp
|
||||
/*.log
|
||||
yarn.lock
|
||||
|
||||
79
CHANGELOG.md
79
CHANGELOG.md
@@ -1,3 +1,82 @@
|
||||
Changes in [1.12.4](https://github.com/element-hq/element-web/releases/tag/v1.12.4) (2025-11-18)
|
||||
================================================================================================
|
||||
## ✨ Features
|
||||
|
||||
* Apply aria-hidden to emoji in SAS verification ([#31204](https://github.com/element-hq/element-web/pull/31204)). Contributed by @t3chguy.
|
||||
* Add options to hide header and composer of room view for the module api ([#31095](https://github.com/element-hq/element-web/pull/31095)). Contributed by @florianduros.
|
||||
* Experimental Module API Additions ([#30863](https://github.com/element-hq/element-web/pull/30863)). Contributed by @dbkr.
|
||||
* Change polls to use fieldset/legend markup ([#31160](https://github.com/element-hq/element-web/pull/31160)). Contributed by @langleyd.
|
||||
* Use compound Button styles for Jitsi button ([#31159](https://github.com/element-hq/element-web/pull/31159)). Contributed by @Half-Shot.
|
||||
* Add FocusLock to emoji picker ([#31146](https://github.com/element-hq/element-web/pull/31146)). Contributed by @langleyd.
|
||||
* Move room name, avatar, and topic to IOpts. ([#30981](https://github.com/element-hq/element-web/pull/30981)). Contributed by @kaylendog.
|
||||
* Add a devtool for looking at users and their devices ([#30983](https://github.com/element-hq/element-web/pull/30983)). Contributed by @uhoreg.
|
||||
|
||||
## 🐛 Bug Fixes
|
||||
|
||||
* Fix room list handling of membership changes ([#31197](https://github.com/element-hq/element-web/pull/31197)). Contributed by @t3chguy.
|
||||
* Fix room list unable to be resized when displayed after a module ([#31186](https://github.com/element-hq/element-web/pull/31186)). Contributed by @florianduros.
|
||||
* Inhibit keyboard highlights in dialogs when effector is not in focus ([#31181](https://github.com/element-hq/element-web/pull/31181)). Contributed by @t3chguy.
|
||||
* Strip mentions from forwarded messages ([#30884](https://github.com/element-hq/element-web/pull/30884)). Contributed by @twassman.
|
||||
* Don't allow pin or edit of messages with a send status ([#31158](https://github.com/element-hq/element-web/pull/31158)). Contributed by @langleyd.
|
||||
* Hide room header buttons if the room hasn't been created yet. ([#31092](https://github.com/element-hq/element-web/pull/31092)). Contributed by @Half-Shot.
|
||||
* Fix screen readers not indicating the emoji picker search field is focused. ([#31128](https://github.com/element-hq/element-web/pull/31128)). Contributed by @langleyd.
|
||||
* Fix emoji picker highlight missing when not active element ([#31148](https://github.com/element-hq/element-web/pull/31148)). Contributed by @t3chguy.
|
||||
* Add relevant aria attribute for selected emoji in the emoji picker ([#31125](https://github.com/element-hq/element-web/pull/31125)). Contributed by @t3chguy.
|
||||
* Fix tooltips within context menu portals being unreliable ([#31129](https://github.com/element-hq/element-web/pull/31129)). Contributed by @t3chguy.
|
||||
* Avoid excessive re-render of room list and member list ([#31131](https://github.com/element-hq/element-web/pull/31131)). Contributed by @florianduros.
|
||||
* Make emoji picker height responsive. ([#31130](https://github.com/element-hq/element-web/pull/31130)). Contributed by @langleyd.
|
||||
* Emoji Picker: Focused emoji does not move with the arrow keys ([#30893](https://github.com/element-hq/element-web/pull/30893)). Contributed by @langleyd.
|
||||
* Fix audio player seek bar position ([#31127](https://github.com/element-hq/element-web/pull/31127)). Contributed by @florianduros.
|
||||
* Add aria label to emoji picker search ([#31126](https://github.com/element-hq/element-web/pull/31126)). Contributed by @langleyd.
|
||||
|
||||
|
||||
Changes in [1.12.3](https://github.com/element-hq/element-web/releases/tag/v1.12.3) (2025-11-04)
|
||||
================================================================================================
|
||||
## 🦖 Deprecations
|
||||
|
||||
* Remove allowVoipWithNoMedia feature flag ([#31087](https://github.com/element-hq/element-web/pull/31087)). Contributed by @Half-Shot.
|
||||
|
||||
## ✨ Features
|
||||
|
||||
* Change module API to be an instance getter ([#31025](https://github.com/element-hq/element-web/pull/31025)). Contributed by @dbkr.
|
||||
|
||||
## 🐛 Bug Fixes
|
||||
|
||||
* Show hover elements when keyboard focus is within an event tile ([#31078](https://github.com/element-hq/element-web/pull/31078)). Contributed by @t3chguy.
|
||||
* Ensure toolbar navigation pattern works in MessageActionBar ([#31080](https://github.com/element-hq/element-web/pull/31080)). Contributed by @t3chguy.
|
||||
* Ensure sent markers are hidden when showing thread summary. ([#31076](https://github.com/element-hq/element-web/pull/31076)). Contributed by @Half-Shot.
|
||||
* Fix translation in dev mode ([#31045](https://github.com/element-hq/element-web/pull/31045)). Contributed by @florianduros.
|
||||
* Fix sort order in space hierarchy ([#30975](https://github.com/element-hq/element-web/pull/30975)). Contributed by @t3chguy.
|
||||
* New Room list: don't display message preview of thread ([#31043](https://github.com/element-hq/element-web/pull/31043)). Contributed by @florianduros.
|
||||
* Revert "A11y: move focus to right panel when opened" ([#30999](https://github.com/element-hq/element-web/pull/30999)). Contributed by @florianduros.
|
||||
* Fix highlights in messages (or search results) breaking links ([#30264](https://github.com/element-hq/element-web/pull/30264)). Contributed by @bojidar-bg.
|
||||
* Add prepare script ([#31030](https://github.com/element-hq/element-web/pull/31030)). Contributed by @dbkr.
|
||||
* Fix html exports by adding SDKContext ([#30987](https://github.com/element-hq/element-web/pull/30987)). Contributed by @t3chguy.
|
||||
|
||||
|
||||
Changes in [1.12.2](https://github.com/element-hq/element-web/releases/tag/v1.12.2) (2025-10-21)
|
||||
================================================================================================
|
||||
## ✨ Features
|
||||
|
||||
* Room List: Extend the viewport to avoid so many black spots when scrolling the room list ([#30867](https://github.com/element-hq/element-web/pull/30867)). Contributed by @langleyd.
|
||||
* Hide calling buttons in room header before a room is created ([#30816](https://github.com/element-hq/element-web/pull/30816)). Contributed by @Half-Shot.
|
||||
* Improve invite dialog ui - Part 2 ([#30836](https://github.com/element-hq/element-web/pull/30836)). Contributed by @florianduros.
|
||||
|
||||
## 🐛 Bug Fixes
|
||||
|
||||
* Fix platform settings race condition and make auto-launch tri-state ([#30977](https://github.com/element-hq/element-web/pull/30977)). Contributed by @t3chguy.
|
||||
* Fix: member count in header and member list ([#30982](https://github.com/element-hq/element-web/pull/30982)). Contributed by @florianduros.
|
||||
* Fix duration of voice message in timeline ([#30973](https://github.com/element-hq/element-web/pull/30973)). Contributed by @florianduros.
|
||||
* Fix voice notes rendering at 00:00 when playback had not begun. ([#30961](https://github.com/element-hq/element-web/pull/30961)). Contributed by @Half-Shot.
|
||||
* Improve handling of animated images, add support for AVIF animations ([#30932](https://github.com/element-hq/element-web/pull/30932)). Contributed by @t3chguy.
|
||||
* Update key storage toggle when key storage status changes ([#30934](https://github.com/element-hq/element-web/pull/30934)). Contributed by @uhoreg.
|
||||
* Fix jitsi widget popout ([#30908](https://github.com/element-hq/element-web/pull/30908)). Contributed by @dbkr.
|
||||
* Improve keyboard navigation on invite dialog ([#30930](https://github.com/element-hq/element-web/pull/30930)). Contributed by @florianduros.
|
||||
* Prefer UIA flows with supported UIA stages ([#30926](https://github.com/element-hq/element-web/pull/30926)). Contributed by @richvdh.
|
||||
* Enhance accessibility of dropdown ([#30928](https://github.com/element-hq/element-web/pull/30928)). Contributed by @florianduros.
|
||||
* Improve accessibility of the `\<AvatarSetting> component ([#30907](https://github.com/element-hq/element-web/pull/30907)). Contributed by @MidhunSureshR.
|
||||
|
||||
|
||||
Changes in [1.12.1](https://github.com/element-hq/element-web/releases/tag/v1.12.1) (2025-10-07)
|
||||
================================================================================================
|
||||
## ✨ Features
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# syntax=docker.io/docker/dockerfile:1.18-labs@sha256:79cdc14e1c220efb546ad14a8ebc816e3277cd72d27195ced5bebdd226dd1025
|
||||
# syntax=docker.io/docker/dockerfile:1.20-labs@sha256:dbcde2ebc4abc8bb5c3c499b9c9a6876842bf5da243951cd2697f921a7aeb6a9
|
||||
|
||||
# Builder
|
||||
FROM --platform=$BUILDPLATFORM node:22-bullseye@sha256:5e638ea282ab9f0224949e8cfc7bb4621710e5d21b19fc3cf6e8884fcb5839f0 AS builder
|
||||
FROM --platform=$BUILDPLATFORM node:24-bullseye@sha256:b36a1eab6bdeb43cf4808370d18b6706452e810e3563b1ce669d2965af3c0464 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:13d1e0acc26b7ce8a40d155473759387e04d1433e73726d4bb49c67bdb197fe5
|
||||
FROM nginxinc/nginx-unprivileged:alpine-slim@sha256:8e23ab31c214ee1d7f832d63b2ee768d5cbc270a94a2cba0752fed93ad83e345
|
||||
|
||||
# Need root user to install packages & manipulate the usr directory
|
||||
USER root
|
||||
|
||||
@@ -18,7 +18,7 @@ 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/src/shared-components). Develop it in storybook!
|
||||
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.
|
||||
4. Views should define the interface of the view model they expect:
|
||||
@@ -35,7 +35,7 @@ This is anywhere your data or business logic comes from. If your view model is a
|
||||
}
|
||||
|
||||
// ViewModel is a type defining the methods needed for `useSyncExternalStore`
|
||||
// https://github.com/element-hq/element-web/blob/develop/src/shared-components/ViewModel.ts
|
||||
// https://github.com/element-hq/element-web/blob/develop/packages/shared-components/src/ViewModel.ts
|
||||
type FooViewModel = ViewModel<FooViewSnapshot> & FooViewActions;
|
||||
|
||||
interface FooViewProps {
|
||||
@@ -54,7 +54,7 @@ This is anywhere your data or business logic comes from. If your view model is a
|
||||
```
|
||||
|
||||
5. Multiple views can share the same view model if necessary.
|
||||
6. A full example is available [here](https://github.com/element-hq/element-web/blob/develop/src/shared-components/audio/AudioPlayerView/AudioPlayerView.tsx)
|
||||
6. A full example is available [here](https://github.com/element-hq/element-web/blob/develop/packages/shared-components/src/audio/AudioPlayerView/AudioPlayerView.tsx)
|
||||
|
||||
#### View Model
|
||||
|
||||
|
||||
@@ -407,7 +407,7 @@ The VoIP and Jitsi options are:
|
||||
If you run your own rageshake server to collect bug reports, the following options may be of interest:
|
||||
|
||||
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://element.io/bugreports/submit` to submit
|
||||
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.
|
||||
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`
|
||||
|
||||
@@ -11,7 +11,7 @@ There are some exceptions like when using localhost, which is considered a [secu
|
||||
1. Download the latest version from <https://github.com/element-hq/element-web/releases>
|
||||
1. Untar the tarball on your web server
|
||||
1. Move (or symlink) the `element-x.x.x` directory to an appropriate name
|
||||
1. Configure the correct caching headers in your webserver (see below)
|
||||
1. Configure the correct caching headers in your webserver (see [README.md](../README.md#caching-requirements))
|
||||
1. Configure the app by copying `config.sample.json` to `config.json` and
|
||||
modifying it. See the [configuration docs](config.md) for details.
|
||||
1. Enter the URL into your browser and log into Element!
|
||||
|
||||
@@ -57,7 +57,7 @@ Then you can deploy it to your cluster with something like `kubectl apply -f my-
|
||||
"https://scalar-staging.vector.im/_matrix/integrations/v1",
|
||||
"https://scalar-staging.vector.im/api"
|
||||
],
|
||||
"bug_report_endpoint_url": "https://element.io/bugreports/submit",
|
||||
"bug_report_endpoint_url": "https://rageshakes.element.io/api/submit",
|
||||
"defaultCountryCode": "GB",
|
||||
"show_labs_settings": false,
|
||||
"features": { },
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"https://scalar-staging.vector.im/_matrix/integrations/v1",
|
||||
"https://scalar-staging.vector.im/api"
|
||||
],
|
||||
"bug_report_endpoint_url": "https://element.io/bugreports/submit",
|
||||
"bug_report_endpoint_url": "https://rageshakes.element.io/api/submit",
|
||||
"uisi_autorageshake_app": "element-auto-uisi",
|
||||
"show_labs_settings": false,
|
||||
"room_directory": {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"https://scalar-staging.vector.im/_matrix/integrations/v1",
|
||||
"https://scalar-staging.vector.im/api"
|
||||
],
|
||||
"bug_report_endpoint_url": "https://element.io/bugreports/submit",
|
||||
"bug_report_endpoint_url": "https://rageshakes.element.io/api/submit",
|
||||
"uisi_autorageshake_app": "element-auto-uisi",
|
||||
"show_labs_settings": true,
|
||||
"room_directory": {
|
||||
|
||||
@@ -17,7 +17,7 @@ const config: Config = {
|
||||
// This is needed to be able to load dual CJS/ESM WASM packages e.g. rust crypto & matrix-wywiwyg
|
||||
customExportConditions: ["browser", "node"],
|
||||
},
|
||||
testMatch: ["<rootDir>/test/**/*-test.[tj]s?(x)", "<rootDir>/src/shared-components/**/*.test.[t]s?(x)"],
|
||||
testMatch: ["<rootDir>/test/**/*-test.[tj]s?(x)"],
|
||||
globalSetup: "<rootDir>/test/globalSetup.ts",
|
||||
setupFiles: ["jest-canvas-mock", "web-streams-polyfill/polyfill"],
|
||||
setupFilesAfterEnv: ["<rootDir>/test/setupTests.ts"],
|
||||
@@ -40,12 +40,14 @@ const config: Config = {
|
||||
"^!!raw-loader!.*": "jest-raw-loader",
|
||||
"recorderWorkletFactory": "<rootDir>/__mocks__/empty.js",
|
||||
"^fetch-mock$": "<rootDir>/node_modules/fetch-mock",
|
||||
"counterpart": "<rootDir>/node_modules/counterpart",
|
||||
},
|
||||
transformIgnorePatterns: [
|
||||
"/node_modules/(?!(mime|matrix-js-sdk|uuid|p-retry|is-network-error|react-merge-refs)).+$",
|
||||
],
|
||||
collectCoverageFrom: [
|
||||
"<rootDir>/src/**/*.{js,ts,tsx}",
|
||||
"<rootDir>/packages/**/*.{js,ts,tsx}",
|
||||
// getSessionLock is piped into a different JS context via stringification, and the coverage functionality is
|
||||
// not available in that contest. So, turn off coverage instrumentation for it.
|
||||
"!<rootDir>/src/utils/SessionLock.ts",
|
||||
|
||||
7
knip.ts
7
knip.ts
@@ -5,6 +5,7 @@ export default {
|
||||
"src/serviceworker/index.ts",
|
||||
"src/workers/*.worker.ts",
|
||||
"src/utils/exportUtils/exportJS.js",
|
||||
"src/vector/localstorage-fix.ts",
|
||||
"scripts/**",
|
||||
"playwright/**",
|
||||
"test/**",
|
||||
@@ -41,6 +42,8 @@ export default {
|
||||
"util",
|
||||
// Embedded into webapp
|
||||
"@element-hq/element-call-embedded",
|
||||
// Transitive dep of jest
|
||||
"jsdom",
|
||||
|
||||
// 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
|
||||
@@ -48,11 +51,13 @@ 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",
|
||||
"wait-on",
|
||||
],
|
||||
ignoreExportsUsedInFile: true,
|
||||
} satisfies KnipConfig;
|
||||
|
||||
@@ -6,7 +6,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import * as YAML from "yaml";
|
||||
import * as fs from "fs";
|
||||
import * as fs from "node:fs";
|
||||
|
||||
export type BuildConfig = {
|
||||
// Dev note: make everything here optional for user safety. Invalid
|
||||
|
||||
@@ -5,11 +5,11 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import * as fs from "fs";
|
||||
import * as childProcess from "child_process";
|
||||
import * as fs from "node:fs";
|
||||
import * as childProcess from "node:child_process";
|
||||
import * as semver from "semver";
|
||||
|
||||
import { type BuildConfig } from "./BuildConfig";
|
||||
import { type BuildConfig } from "./BuildConfig.ts";
|
||||
|
||||
// This expects to be run from ./scripts/install.ts
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { readBuildConfig } from "../BuildConfig";
|
||||
import { installer } from "../installer";
|
||||
import { readBuildConfig } from "../BuildConfig.ts";
|
||||
import { installer } from "../installer.ts";
|
||||
|
||||
const buildConf = readBuildConfig();
|
||||
installer(buildConf);
|
||||
|
||||
76
package.json
76
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "element-web",
|
||||
"version": "1.12.1",
|
||||
"version": "1.12.4",
|
||||
"description": "Element: the future of secure communication",
|
||||
"author": "New Vector Ltd.",
|
||||
"repository": {
|
||||
@@ -29,7 +29,7 @@
|
||||
"UserFriendlyError"
|
||||
],
|
||||
"scripts": {
|
||||
"i18n": "matrix-gen-i18n src res packages/shared-components && yarn i18n:sort && yarn i18n:lint",
|
||||
"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: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",
|
||||
@@ -38,16 +38,16 @@
|
||||
"clean": "rimraf lib webapp",
|
||||
"build": "yarn clean && yarn build:genfiles && yarn build:bundle",
|
||||
"build-stats": "yarn clean && yarn build:genfiles && yarn build:bundle-stats",
|
||||
"build:res": "ts-node scripts/copy-res.ts",
|
||||
"build:res": "node scripts/copy-res.ts",
|
||||
"build:genfiles": "yarn build:res && yarn build:module_system",
|
||||
"build:modernizr": "modernizr -c .modernizr.json -d src/vector/modernizr.js",
|
||||
"build:bundle": "webpack --progress --mode production",
|
||||
"build:bundle-stats": "webpack --progress --mode production --json > webpack-stats.json",
|
||||
"build:module_system": "ts-node --project ./tsconfig.module_system.json module_system/scripts/install.ts",
|
||||
"build:module_system": "node module_system/scripts/install.ts",
|
||||
"dist": "./scripts/package.sh",
|
||||
"start": "concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n modules,res \"yarn build:module_system\" \"yarn build:res\" && concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n res,element-js \"yarn start:res\" \"yarn start:js\"",
|
||||
"start:https": "concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n res,element-js \"yarn start:res\" \"yarn start:js --server-type https\"",
|
||||
"start:res": "ts-node scripts/copy-res.ts -w",
|
||||
"start:res": "node scripts/copy-res.ts -w",
|
||||
"start:js": "webpack serve --output-path webapp --output-filename=bundles/_dev_/[name].js --output-chunk-filename=bundles/_dev_/[name].js --mode development",
|
||||
"lint": "yarn lint:types && yarn lint:js && yarn lint:style && yarn lint:workflows",
|
||||
"lint:js": "eslint --max-warnings 0 src test playwright module_system && prettier --check .",
|
||||
@@ -65,28 +65,30 @@
|
||||
"coverage": "yarn test --coverage",
|
||||
"analyse:webpack-bundles": "webpack-bundle-analyzer webpack-stats.json webapp",
|
||||
"update:jitsi": "curl -s https://meet.element.io/libs/external_api.min.js > ./res/jitsi_external_api.min.js",
|
||||
"install": "yarn --cwd packages/shared-components install --frozen-lockfile",
|
||||
"postinstall": "patch-package"
|
||||
},
|
||||
"resolutions": {
|
||||
"**/pretty-format/react-is": "19.1.1",
|
||||
"@playwright/test": "1.54.2",
|
||||
"@types/react": "19.1.14",
|
||||
"@types/react-dom": "19.1.9",
|
||||
"oidc-client-ts": "3.3.0",
|
||||
"**/pretty-format/react-is": "19.2.0",
|
||||
"@playwright/test": "1.57.0",
|
||||
"@types/react": "19.2.6",
|
||||
"@types/react-dom": "19.2.3",
|
||||
"oidc-client-ts": "3.4.1",
|
||||
"jwt-decode": "4.0.0",
|
||||
"caniuse-lite": "1.0.30001745",
|
||||
"caniuse-lite": "1.0.30001756",
|
||||
"testcontainers": "^11.0.0",
|
||||
"wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0",
|
||||
"wrap-ansi": "npm:wrap-ansi@^7.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@element-hq/element-web-module-api": "1.4.1",
|
||||
"@fontsource/inconsolata": "^5",
|
||||
"@element-hq/element-web-module-api": "1.6.0",
|
||||
"@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.29.2",
|
||||
"@matrix-org/emojibase-bindings": "^1.3.4",
|
||||
"@matrix-org/analytics-events": "^0.30.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",
|
||||
@@ -103,12 +105,11 @@
|
||||
"browserslist": "^4.23.2",
|
||||
"classnames": "^2.2.6",
|
||||
"commonmark": "^0.31.0",
|
||||
"counterpart": "^0.18.6",
|
||||
"css-tree": "^3.0.0",
|
||||
"diff-dom": "^5.0.0",
|
||||
"diff-match-patch": "^1.0.5",
|
||||
"domutils": "^3.2.2",
|
||||
"emojibase-regex": "15.3.2",
|
||||
"emojibase-regex": "^17.0.0",
|
||||
"escape-html": "^1.0.3",
|
||||
"file-saver": "^2.0.5",
|
||||
"filesize": "11.0.13",
|
||||
@@ -118,10 +119,11 @@
|
||||
"html-entities": "^2.0.0",
|
||||
"html-react-parser": "^5.2.2",
|
||||
"is-ip": "^3.1.0",
|
||||
"js-xxhash": "^4.0.0",
|
||||
"js-xxhash": "^5.0.0",
|
||||
"jsrsasign": "^11.0.0",
|
||||
"jszip": "^3.7.0",
|
||||
"katex": "^0.16.0",
|
||||
"linkify-html": "4.3.2",
|
||||
"linkify-react": "4.3.2",
|
||||
"linkify-string": "4.3.2",
|
||||
"linkifyjs": "4.3.2",
|
||||
@@ -130,14 +132,14 @@
|
||||
"matrix-encrypt-attachment": "^1.0.3",
|
||||
"matrix-events-sdk": "0.0.1",
|
||||
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
|
||||
"matrix-widget-api": "^1.10.0",
|
||||
"matrix-widget-api": "^1.14.0",
|
||||
"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.268.6",
|
||||
"posthog-js": "1.297.2",
|
||||
"qrcode": "1.5.4",
|
||||
"re-resizable": "6.11.2",
|
||||
"react": "^19.0.0",
|
||||
@@ -179,14 +181,14 @@
|
||||
"@babel/preset-react": "^7.12.10",
|
||||
"@babel/preset-typescript": "^7.12.7",
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@casualbot/jest-sonar-reporter": "2.2.7",
|
||||
"@element-hq/element-call-embedded": "0.16.0",
|
||||
"@casualbot/jest-sonar-reporter": "2.4.0",
|
||||
"@element-hq/element-call-embedded": "0.16.1",
|
||||
"@element-hq/element-web-playwright-common": "^2.0.0",
|
||||
"@peculiar/webcrypto": "^1.4.3",
|
||||
"@playwright/test": "^1.50.1",
|
||||
"@principalstudio/html-webpack-inject-preload": "^1.2.7",
|
||||
"@rrweb/types": "^2.0.0-alpha.18",
|
||||
"@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",
|
||||
@@ -202,7 +204,7 @@
|
||||
"@types/express": "^5.0.0",
|
||||
"@types/file-saver": "^2.0.3",
|
||||
"@types/glob-to-regexp": "^0.4.1",
|
||||
"@types/jest": "29.5.12",
|
||||
"@types/jest": "30.0.0",
|
||||
"@types/jitsi-meet": "^2.0.2",
|
||||
"@types/jsrsasign": "^10.5.4",
|
||||
"@types/katex": "^0.16.0",
|
||||
@@ -213,9 +215,9 @@
|
||||
"@types/node-fetch": "^2.6.2",
|
||||
"@types/pako": "^2.0.0",
|
||||
"@types/qrcode": "^1.3.5",
|
||||
"@types/react": "19.1.14",
|
||||
"@types/react": "19.2.6",
|
||||
"@types/react-beautiful-dnd": "^13.0.0",
|
||||
"@types/react-dom": "19.1.9",
|
||||
"@types/react-dom": "19.2.3",
|
||||
"@types/react-transition-group": "^4.4.0",
|
||||
"@types/sanitize-html": "2.16.0",
|
||||
"@types/sdp-transform": "^2.4.10",
|
||||
@@ -224,7 +226,7 @@
|
||||
"@types/ua-parser-js": "^0.7.36",
|
||||
"@typescript-eslint/eslint-plugin": "^8.19.0",
|
||||
"@typescript-eslint/parser": "^8.19.0",
|
||||
"babel-jest": "^29.0.0",
|
||||
"babel-jest": "^30.0.0",
|
||||
"babel-loader": "^10.0.0",
|
||||
"babel-plugin-jsx-remove-data-test-id": "^3.0.0",
|
||||
"blob-polyfill": "^9.0.0",
|
||||
@@ -239,14 +241,14 @@
|
||||
"eslint": "8.57.1",
|
||||
"eslint-config-google": "^0.14.0",
|
||||
"eslint-config-prettier": "^10.0.0",
|
||||
"eslint-plugin-deprecate": "0.8.5",
|
||||
"eslint-plugin-deprecate": "0.8.7",
|
||||
"eslint-plugin-import": "^2.25.4",
|
||||
"eslint-plugin-jest": "^28.0.0",
|
||||
"eslint-plugin-jest": "^29.0.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.5.1",
|
||||
"eslint-plugin-matrix-org": "^2.0.2",
|
||||
"eslint-plugin-matrix-org": "^3.0.0",
|
||||
"eslint-plugin-react": "^7.28.0",
|
||||
"eslint-plugin-react-compiler": "^19.0.0-beta-df7b47d-20241124",
|
||||
"eslint-plugin-react-hooks": "^5.0.0",
|
||||
"eslint-plugin-react-hooks": "^7.0.0",
|
||||
"eslint-plugin-unicorn": "^56.0.0",
|
||||
"express": "^5.0.0",
|
||||
"fake-indexeddb": "^6.0.0",
|
||||
@@ -256,10 +258,10 @@
|
||||
"html-webpack-plugin": "^5.5.3",
|
||||
"husky": "^9.0.0",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"jest": "^29.6.2",
|
||||
"jest": "^30.0.0",
|
||||
"jest-canvas-mock": "^2.5.2",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"jest-mock": "^29.6.2",
|
||||
"jest-environment-jsdom": "^30.0.0",
|
||||
"jest-mock": "^30.0.0",
|
||||
"jest-raw-loader": "^1.0.1",
|
||||
"jsqr": "^1.4.0",
|
||||
"knip": "^5.36.2",
|
||||
@@ -287,18 +289,18 @@
|
||||
"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-value-no-unknown-custom-properties": "^6.0.1",
|
||||
"terser-webpack-plugin": "^5.3.9",
|
||||
"testcontainers": "^11.0.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "5.8.3",
|
||||
"util": "^0.12.5",
|
||||
"web-streams-polyfill": "^4.0.0",
|
||||
"webpack": "^5.89.0",
|
||||
"webpack-bundle-analyzer": "^4.8.0",
|
||||
"webpack-bundle-analyzer": "^5.0.0",
|
||||
"webpack-cli": "^6.0.0",
|
||||
"webpack-dev-server": "^5.0.0",
|
||||
"webpack-retry-chunk-load-plugin": "^3.1.1",
|
||||
@@ -311,7 +313,7 @@
|
||||
"relativePaths": true
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
"node": ">=22.18"
|
||||
},
|
||||
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
/*
|
||||
Copyright 2025 Element Creations Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
plugins: ["matrix-org", "eslint-plugin-react-compiler"],
|
||||
extends: [
|
||||
"plugin:matrix-org/babel",
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
dist/
|
||||
i18n/i18nKeys.d.ts
|
||||
|
||||
@@ -5,17 +5,14 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { waitForPageReady } from "@storybook/test-runner";
|
||||
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/`;
|
||||
|
||||
/**
|
||||
* @type {import('@storybook/test-runner').TestRunnerConfig}
|
||||
*/
|
||||
const config = {
|
||||
setup(page) {
|
||||
const config: TestRunnerConfig = {
|
||||
setup() {
|
||||
expect.extend({ toMatchImageSnapshot });
|
||||
},
|
||||
async postVisit(page, context) {
|
||||
21
packages/shared-components/babel.config.js
Normal file
21
packages/shared-components/babel.config.js
Normal file
@@ -0,0 +1,21 @@
|
||||
module.exports = {
|
||||
sourceMaps: true,
|
||||
presets: [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
include: ["@babel/plugin-transform-class-properties"],
|
||||
},
|
||||
],
|
||||
["@babel/preset-typescript", { allowDeclareFields: true }],
|
||||
"@babel/preset-react",
|
||||
],
|
||||
plugins: [
|
||||
"@babel/plugin-proposal-export-default-from",
|
||||
"@babel/plugin-transform-numeric-separator",
|
||||
"@babel/plugin-transform-object-rest-spread",
|
||||
"@babel/plugin-transform-optional-chaining",
|
||||
"@babel/plugin-transform-nullish-coalescing-operator",
|
||||
"@babel/plugin-transform-runtime",
|
||||
],
|
||||
};
|
||||
58
packages/shared-components/jest.config.ts
Normal file
58
packages/shared-components/jest.config.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
Copyright 2025 Element Creations Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { env } from "process";
|
||||
|
||||
import type { Config } from "jest";
|
||||
|
||||
const config: Config = {
|
||||
testEnvironment: "jsdom",
|
||||
testEnvironmentOptions: {
|
||||
url: "http://localhost/",
|
||||
},
|
||||
testMatch: ["<rootDir>/src/**/*.test.[tj]s?(x)"],
|
||||
setupFilesAfterEnv: ["<rootDir>/src/test/setupTests.ts"],
|
||||
moduleNameMapper: {
|
||||
// Support CSS module
|
||||
"\\.(module.css)$": "identity-obj-proxy",
|
||||
"\\.(css|scss|pcss)$": "<rootDir>/__mocks__/cssMock.js",
|
||||
"\\.(gif|png|ttf|woff2)$": "<rootDir>/__mocks__/imageMock.js",
|
||||
"\\.svg$": "<rootDir>/__mocks__/svg.js",
|
||||
"\\$webapp/i18n/languages.json": "<rootDir>/../../__mocks__/languages.json",
|
||||
"^react$": "<rootDir>/node_modules/react",
|
||||
"^react-dom$": "<rootDir>/node_modules/react-dom",
|
||||
"waveWorker\\.min\\.js": "<rootDir>/__mocks__/empty.js",
|
||||
"context-filter-polyfill": "<rootDir>/__mocks__/empty.js",
|
||||
"workers/(.+)Factory": "<rootDir>/__mocks__/workerFactoryMock.js",
|
||||
},
|
||||
transformIgnorePatterns: [
|
||||
"/node_modules/(?!(mime|matrix-js-sdk|uuid|p-retry|is-network-error|react-merge-refs|@storybook|storybook)).+$",
|
||||
],
|
||||
collectCoverageFrom: [
|
||||
"<rootDir>/src/**/*.{js,ts,tsx}",
|
||||
"<rootDir>/packages/**/*.{js,ts,tsx}",
|
||||
// Coverage chokes on type definition files
|
||||
"!<rootDir>/src/**/*.d.ts",
|
||||
],
|
||||
coverageReporters: ["text-summary", "lcov"],
|
||||
testResultsProcessor: "@casualbot/jest-sonar-reporter",
|
||||
prettierPath: null,
|
||||
moduleDirectories: ["node_modules", "./src/test/utils"],
|
||||
};
|
||||
|
||||
// if we're running under GHA, enable the GHA reporter
|
||||
if (env["GITHUB_ACTIONS"] !== undefined) {
|
||||
const reporters: Config["reporters"] = [["github-actions", { silent: false }], "summary"];
|
||||
|
||||
// if we're running against the develop branch, also enable the slow test reporter
|
||||
if (env["GITHUB_REF"] == "refs/heads/develop") {
|
||||
reporters.push("<rootDir>/../../test/slowReporter.cjs");
|
||||
}
|
||||
config.reporters = reporters;
|
||||
}
|
||||
|
||||
export default config;
|
||||
@@ -1,22 +1,41 @@
|
||||
{
|
||||
"name": "element-web-shared-components",
|
||||
"version": "1.12.1",
|
||||
"name": "@element-hq/web-shared-components",
|
||||
"version": "0.0.0-test.8",
|
||||
"description": "Shared components for Element",
|
||||
"author": "New Vector Ltd.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/element-hq/element-web"
|
||||
},
|
||||
"license": "SEE LICENSE IN README.md",
|
||||
"exports": {
|
||||
".": {
|
||||
"require": {
|
||||
"style": "./dist/element-web-shared-components.css",
|
||||
"types": "./dist/element-web-shared-components.d.ts",
|
||||
"default": "./dist/element-web-shared-components.umd.js"
|
||||
},
|
||||
"import": {
|
||||
"style": "./dist/element-web-shared-components.css",
|
||||
"types": "./dist/element-web-shared-components.d.ts",
|
||||
"default": "./dist/element-web-shared-components.mjs"
|
||||
}
|
||||
},
|
||||
"./dist/element-web-shared-components.css": {
|
||||
"require": "./dist/element-web-shared-components.css",
|
||||
"import": "./dist/element-web-shared-components.css"
|
||||
}
|
||||
},
|
||||
"types": "dist/element-web-shared-components.d.ts",
|
||||
"files": [
|
||||
"lib",
|
||||
"dist",
|
||||
"src",
|
||||
"LICENSE",
|
||||
"README.md",
|
||||
"package.json"
|
||||
],
|
||||
"scripts": {
|
||||
"postinstall": "patch-package",
|
||||
"test": "jest",
|
||||
"prepare": "patch-package && yarn --cwd ../.. build:res && node scripts/gatherTranslationKeys.ts && vite build",
|
||||
"storybook": "storybook dev -p 6007",
|
||||
"build-storybook": "storybook build",
|
||||
"lint": "yarn lint:types && yarn lint:js",
|
||||
@@ -26,27 +45,50 @@
|
||||
"test:storybook:ci": "concurrently -k -s first -n \"SB,TEST\" \"yarn storybook --no-open\" \"wait-on tcp:6007 && yarn test-storybook --url http://localhost:6007/ --ci --maxWorkers=2\"",
|
||||
"test:storybook:update": "playwright-screenshots --entrypoint yarn --with-node-modules && playwright-screenshots --entrypoint /work/node_modules/.bin/test-storybook --with-node-modules --url http://host.docker.internal:6007/ --updateSnapshot"
|
||||
},
|
||||
"dependencies": {},
|
||||
"dependencies": {
|
||||
"@vector-im/compound-design-tokens": "^6.3.0",
|
||||
"classnames": "^2.5.1",
|
||||
"counterpart": "^0.18.6",
|
||||
"lodash": "^4.17.21",
|
||||
"matrix-web-i18n": "^3.4.0",
|
||||
"patch-package": "^8.0.1",
|
||||
"react-merge-refs": "^3.0.2",
|
||||
"temporal-polyfill": "^0.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@storybook/addon-a11y": "^9.1.10",
|
||||
"@storybook/addon-designs": "^10.0.2",
|
||||
"@storybook/addon-docs": "^9.1.10",
|
||||
"@storybook/icons": "^1.6.0",
|
||||
"@storybook/react-vite": "^9.1.10",
|
||||
"@storybook/test-runner": "^0.23.0",
|
||||
"@element-hq/element-web-playwright-common": "^2.0.0",
|
||||
"@playwright/test": "^1.50.1",
|
||||
"@storybook/addon-a11y": "^10.0.7",
|
||||
"@storybook/addon-designs": "^11.0.1",
|
||||
"@storybook/addon-docs": "^10.0.7",
|
||||
"@storybook/icons": "^2.0.0",
|
||||
"@storybook/react-vite": "^10.0.7",
|
||||
"@storybook/test-runner": "^0.24.1",
|
||||
"@testing-library/dom": "^10.4.1",
|
||||
"@testing-library/react": "^16.3.0",
|
||||
"@types/counterpart": "^0.18.4",
|
||||
"@types/jest-image-snapshot": "^6.4.0",
|
||||
"@types/lodash": "^4.17.20",
|
||||
"@types/react": "^19.2.2",
|
||||
"concurrently": "^9.2.1",
|
||||
"eslint": "8",
|
||||
"eslint-plugin-storybook": "^9.1.10",
|
||||
"eslint-plugin-matrix-org": "^3.0.0",
|
||||
"eslint-plugin-storybook": "^10.0.7",
|
||||
"jest": "^30.2.0",
|
||||
"jest-image-snapshot": "^6.5.1",
|
||||
"patch-package": "^8.0.1",
|
||||
"prettier": "^3.6.2",
|
||||
"storybook": "^9.1.10",
|
||||
"storybook": "^10.0.7",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "^7.1.9",
|
||||
"vite-plugin-dts": "^4.5.4",
|
||||
"vite-plugin-node-polyfills": "^0.24.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
},
|
||||
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
||||
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e",
|
||||
"peerDependencies": {
|
||||
"@vector-im/compound-web": "^8.2.5"
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 9.5 KiB |
67
packages/shared-components/scripts/gatherTranslationKeys.ts
Normal file
67
packages/shared-components/scripts/gatherTranslationKeys.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
Copyright 2025 Element Creations Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
// Gathers all the translation keys from element-web's en_EN.json into a TypeScript type definition file
|
||||
// that exports a type `TranslationKey` which is a union of all supported translation keys.
|
||||
// This prevents having to import the json file and make typescript do the work as this results in vite-dts
|
||||
// generating an import to the json file in the .d.ts which doesn't work at runtime: this way, the type
|
||||
// gets put into the bundle.
|
||||
// XXX: It should *not* be in the 'src' directory, being a generated file, but if it isn't then the type
|
||||
// bundler won't bundle the types and will leave the file as a relative import, which will break.
|
||||
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
import { dirname } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const i18nStringsPath = path.resolve(__dirname, "../../../src/i18n/strings/en_EN.json");
|
||||
const outPath = path.resolve(__dirname, "../src/i18nKeys.d.ts");
|
||||
|
||||
function gatherKeys(obj: any, prefix: string[] = []): string[] {
|
||||
if (typeof obj !== "object" || obj === null) return [];
|
||||
let keys: string[] = [];
|
||||
for (const key of Object.keys(obj)) {
|
||||
const value = obj[key];
|
||||
|
||||
// add the path (for both leaves and intermediates as then we include plurals)
|
||||
keys.push([...prefix, key].join("|"));
|
||||
if (typeof value === "object" && value !== null) {
|
||||
// If the value is an object, recurse
|
||||
keys = keys.concat(gatherKeys(value, [...prefix, key]));
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
function main() {
|
||||
const json = JSON.parse(fs.readFileSync(i18nStringsPath, "utf8"));
|
||||
const keys = gatherKeys(json);
|
||||
const typeDef =
|
||||
"/*\n" +
|
||||
" * Copyright 2025 Element Creations Ltd.\n" +
|
||||
" *\n" +
|
||||
" * SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial\n" +
|
||||
" * Please see LICENSE files in the repository root for full details.\n" +
|
||||
" */\n" +
|
||||
"\n" +
|
||||
"// This file is auto-generated by gatherTranslationKeys.ts\n" +
|
||||
"// Do not edit manually.\n\n" +
|
||||
"export type TranslationKey =\n" +
|
||||
keys.map((k) => ` | \"${k}\"`).join("\n") +
|
||||
";\n";
|
||||
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
||||
fs.writeFileSync(outPath, typeDef, "utf8");
|
||||
console.log(`Wrote ${keys.length} keys to ${outPath}`);
|
||||
}
|
||||
|
||||
if (import.meta.url.startsWith("file:")) {
|
||||
const modulePath = fileURLToPath(import.meta.url);
|
||||
if (process.argv[1] === modulePath) {
|
||||
main();
|
||||
}
|
||||
}
|
||||
@@ -1,52 +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 React, { type JSX, useMemo, type ComponentType } from "react";
|
||||
import { omitBy, pickBy } from "lodash";
|
||||
|
||||
import { MockViewModel } from "./MockViewModel";
|
||||
import { type ViewModel } from "./ViewModel";
|
||||
|
||||
interface ViewWrapperProps<V> {
|
||||
/**
|
||||
* The component to render, which should accept a `vm` prop of type `V`.
|
||||
*/
|
||||
Component: ComponentType<{ vm: V }>;
|
||||
/**
|
||||
* The props to pass to the component, which can include both snapshot data and actions.
|
||||
*/
|
||||
props: Record<string, any>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper component that creates a view model instance and passes it to the specified component.
|
||||
* This is useful for testing components in isolation with a mocked view model and allows to use primitive types in stories.
|
||||
*
|
||||
* Props is parsed and split into snapshot and actions. Where values that are functions (`typeof Function`) are considered actions and the rest is considered the snapshot.
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* <ViewWrapper<SnapshotType, ViewModelType> props={Snapshot&Actions} Component={MyComponent} />
|
||||
* ```
|
||||
*/
|
||||
export function ViewWrapper<T, V extends ViewModel<T>>({
|
||||
props,
|
||||
Component,
|
||||
}: Readonly<ViewWrapperProps<V>>): JSX.Element {
|
||||
const vm = useMemo(() => {
|
||||
const isFunction = (value: any): value is typeof Function => typeof value === typeof Function;
|
||||
const snapshot = omitBy(props, isFunction) as T;
|
||||
const actions = pickBy(props, isFunction);
|
||||
|
||||
const vm = new MockViewModel<T>(snapshot);
|
||||
Object.assign(vm, actions);
|
||||
|
||||
return vm as unknown as V;
|
||||
}, [props]);
|
||||
|
||||
return <Component vm={vm} />;
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
.audioPlayer {
|
||||
padding: var(--cpd-space-4x) var(--cpd-space-3x) var(--cpd-space-3x) var(--cpd-space-3x);
|
||||
padding: var(--cpd-space-4x) var(--cpd-space-3x) var(--cpd-space-3x) var(--cpd-space-3x) !important;
|
||||
}
|
||||
|
||||
.mediaInfo {
|
||||
|
||||
@@ -9,18 +9,18 @@ import React, { type JSX } from "react";
|
||||
import { fn } from "storybook/test";
|
||||
|
||||
import type { Meta, StoryFn } from "@storybook/react-vite";
|
||||
import {
|
||||
AudioPlayerView,
|
||||
type AudioPlayerViewActions,
|
||||
type AudioPlayerViewSnapshot,
|
||||
type AudioPlayerViewModel,
|
||||
} from "./AudioPlayerView";
|
||||
import { ViewWrapper } from "../../ViewWrapper";
|
||||
import { AudioPlayerView, type AudioPlayerViewActions, type AudioPlayerViewSnapshot } from "./AudioPlayerView";
|
||||
import { useMockedViewModel } from "../../useMockedViewModel";
|
||||
|
||||
type AudioPlayerProps = AudioPlayerViewSnapshot & AudioPlayerViewActions;
|
||||
const AudioPlayerViewWrapper = (props: AudioPlayerProps): JSX.Element => (
|
||||
<ViewWrapper<AudioPlayerViewSnapshot, AudioPlayerViewModel> Component={AudioPlayerView} props={props} />
|
||||
);
|
||||
const AudioPlayerViewWrapper = ({ togglePlay, onKeyDown, onSeekbarChange, ...rest }: AudioPlayerProps): JSX.Element => {
|
||||
const vm = useMockedViewModel(rest, {
|
||||
togglePlay,
|
||||
onKeyDown,
|
||||
onSeekbarChange,
|
||||
});
|
||||
return <AudioPlayerView vm={vm} />;
|
||||
};
|
||||
|
||||
export default {
|
||||
title: "Audio/AudioPlayerView",
|
||||
|
||||
@@ -13,7 +13,7 @@ import { fireEvent } from "@testing-library/dom";
|
||||
|
||||
import * as stories from "./AudioPlayerView.stories.tsx";
|
||||
import { AudioPlayerView, type AudioPlayerViewActions, type AudioPlayerViewSnapshot } from "./AudioPlayerView";
|
||||
import { MockViewModel } from "../../MockViewModel";
|
||||
import { MockViewModel } from "../../viewmodel/MockViewModel.ts";
|
||||
|
||||
const { Default, NoMediaName, NoSize, HasError } = composeStories(stories);
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
import React, { type ChangeEventHandler, type JSX, type KeyboardEventHandler, type MouseEventHandler } from "react";
|
||||
|
||||
import { type ViewModel } from "../../ViewModel";
|
||||
import { type ViewModel } from "../../viewmodel/ViewModel";
|
||||
import { useViewModel } from "../../useViewModel";
|
||||
import { MediaBody } from "../../message-body/MediaBody";
|
||||
import { Flex } from "../../utils/Flex";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`AudioPlayerView renders the audio player in default state 1`] = `
|
||||
<div>
|
||||
@@ -15,7 +15,7 @@ exports[`AudioPlayerView renders the audio player in default state 1`] = `
|
||||
<button
|
||||
aria-disabled="false"
|
||||
aria-label="Play"
|
||||
aria-labelledby="«r0»"
|
||||
aria-labelledby="_r_0_"
|
||||
class="_icon-button_1pz9o_8 button"
|
||||
data-kind="primary"
|
||||
role="button"
|
||||
@@ -106,7 +106,7 @@ exports[`AudioPlayerView renders the audio player in error state 1`] = `
|
||||
<button
|
||||
aria-disabled="false"
|
||||
aria-label="Play"
|
||||
aria-labelledby="«ri»"
|
||||
aria-labelledby="_r_i_"
|
||||
class="_icon-button_1pz9o_8 button"
|
||||
data-kind="primary"
|
||||
role="button"
|
||||
@@ -202,7 +202,7 @@ exports[`AudioPlayerView renders the audio player without media name 1`] = `
|
||||
<button
|
||||
aria-disabled="false"
|
||||
aria-label="Play"
|
||||
aria-labelledby="«r6»"
|
||||
aria-labelledby="_r_6_"
|
||||
class="_icon-button_1pz9o_8 button"
|
||||
data-kind="primary"
|
||||
role="button"
|
||||
@@ -293,7 +293,7 @@ exports[`AudioPlayerView renders the audio player without size 1`] = `
|
||||
<button
|
||||
aria-disabled="false"
|
||||
aria-label="Play"
|
||||
aria-labelledby="«rc»"
|
||||
aria-labelledby="_r_c_"
|
||||
class="_icon-button_1pz9o_8 button"
|
||||
data-kind="primary"
|
||||
role="button"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`Clock renders the clock 1`] = `
|
||||
<div>
|
||||
|
||||
@@ -6,6 +6,6 @@
|
||||
*/
|
||||
|
||||
.button {
|
||||
border-radius: 32px;
|
||||
background-color: var(--cpd-color-bg-subtle-primary);
|
||||
border-radius: 32px !important;
|
||||
background-color: var(--cpd-color-bg-subtle-primary) !important;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`PlayPauseButton renders the button in default state 1`] = `
|
||||
<div>
|
||||
<button
|
||||
aria-disabled="false"
|
||||
aria-label="Play"
|
||||
aria-labelledby="«r0»"
|
||||
aria-labelledby="_r_0_"
|
||||
class="_icon-button_1pz9o_8 button"
|
||||
data-kind="primary"
|
||||
role="button"
|
||||
@@ -37,7 +37,7 @@ exports[`PlayPauseButton renders the button in playing state 1`] = `
|
||||
<button
|
||||
aria-disabled="false"
|
||||
aria-label="Pause"
|
||||
aria-labelledby="«r6»"
|
||||
aria-labelledby="_r_6_"
|
||||
class="_icon-button_1pz9o_8 button"
|
||||
data-kind="primary"
|
||||
role="button"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`Seekbar renders the clock 1`] = `
|
||||
<div>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { type Meta, type StoryObj } from "@storybook/react-vite/*";
|
||||
import { type Meta, type StoryObj } from "@storybook/react-vite";
|
||||
|
||||
import { AvatarWithDetails } from "./AvatarWithDetails";
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`AvatarWithDetails renders a textual event 1`] = `
|
||||
<div>
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
:root {
|
||||
--cpd-color-gradient-critical-linear: linear-gradient(
|
||||
180deg,
|
||||
var(--cpd-color-alpha-red-500) 0%,
|
||||
var(--cpd-color-alpha-red-400) 20%,
|
||||
var(--cpd-color-alpha-red-300) 40%,
|
||||
var(--cpd-color-alpha-red-200) 60%,
|
||||
var(--cpd-color-alpha-red-100) 80%,
|
||||
var(--cpd-color-transparent) 100%
|
||||
);
|
||||
}
|
||||
|
||||
.banner {
|
||||
container-type: inline-size;
|
||||
container-name: banner;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: start;
|
||||
gap: var(--cpd-space-3x);
|
||||
padding: var(--cpd-space-4x);
|
||||
|
||||
border-top: 1px solid var(--cpd-color-gray-400);
|
||||
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.banner[data-type="success"] {
|
||||
background: var(--cpd-color-gradient-subtle-linear);
|
||||
border-color: var(--cpd-color-green-900);
|
||||
}
|
||||
|
||||
.banner[data-type="critical"] {
|
||||
background: var(--cpd-color-gradient-critical-linear);
|
||||
border-color: var(--cpd-color-border-critical-primary);
|
||||
}
|
||||
|
||||
.banner[data-type="info"] {
|
||||
background: var(--cpd-color-gradient-info-linear);
|
||||
border-color: var(--cpd-color-blue-900);
|
||||
}
|
||||
|
||||
.banner[data-type="info"] :is(svg) {
|
||||
color: var(--cpd-color-blue-900);
|
||||
}
|
||||
|
||||
.banner[data-type="success"] :is(.content, svg) {
|
||||
color: var(--cpd-color-green-900);
|
||||
}
|
||||
|
||||
.banner[data-type="critical"] :is(.content, svg) {
|
||||
color: var(--cpd-color-red-900);
|
||||
}
|
||||
|
||||
.banner p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.icon {
|
||||
/* lock icon dimensions */
|
||||
min-width: 32px;
|
||||
min-height: 32px;
|
||||
max-width: 32px;
|
||||
max-height: 32px;
|
||||
|
||||
margin: 4px;
|
||||
|
||||
/* centre svg icons, as they are not full width */
|
||||
flex: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.icon img {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.actions {
|
||||
margin-left: auto;
|
||||
|
||||
flex: 0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: var(--cpd-space-1x);
|
||||
align-self: center;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { fn } from "storybook/test";
|
||||
import { type Meta, type StoryObj } from "@storybook/react-vite";
|
||||
import { Button } from "@vector-im/compound-web";
|
||||
|
||||
import { Banner } from "./Banner";
|
||||
import { _t } from "../../utils/i18n";
|
||||
|
||||
const meta = {
|
||||
title: "room/Banner",
|
||||
component: Banner,
|
||||
tags: ["autodocs"],
|
||||
args: {
|
||||
children: <p>Hello! This is a status banner.</p>,
|
||||
onClose: fn(),
|
||||
},
|
||||
} satisfies Meta<typeof Banner>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Default: Story = {};
|
||||
export const Info: Story = {
|
||||
args: {
|
||||
type: "info",
|
||||
},
|
||||
};
|
||||
export const Success: Story = {
|
||||
args: {
|
||||
type: "success",
|
||||
},
|
||||
};
|
||||
export const Critical: Story = {
|
||||
args: {
|
||||
type: "critical",
|
||||
},
|
||||
};
|
||||
export const WithAction: Story = {
|
||||
args: {
|
||||
children: (
|
||||
<p>
|
||||
{_t(
|
||||
"encryption|pinned_identity_changed",
|
||||
{ displayName: "Alice", userId: "@alice:example.org" },
|
||||
{
|
||||
a: (sub) => <a href="https://example.org">{sub}</a>,
|
||||
b: (sub) => <b>{sub}</b>,
|
||||
},
|
||||
)}
|
||||
</p>
|
||||
),
|
||||
actions: <Button kind="primary">{_t("encryption|withdraw_verification_action")}</Button>,
|
||||
},
|
||||
};
|
||||
|
||||
export const WithAvatarImage: Story = {
|
||||
args: {
|
||||
avatar: <img alt="Example" src="https://picsum.photos/32/32" />,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { render } from "jest-matrix-react";
|
||||
import { composeStories } from "@storybook/react-vite";
|
||||
|
||||
import * as stories from "./Banner.stories.tsx";
|
||||
|
||||
const { Default, Info, Success, WithAction, WithAvatarImage, Critical } = composeStories(stories);
|
||||
|
||||
describe("AvatarWithDetails", () => {
|
||||
it("renders a default banner", () => {
|
||||
const { container } = render(<Default />);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
it("renders a info banner", () => {
|
||||
const { container } = render(<Info />);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
it("renders a success banner", () => {
|
||||
const { container } = render(<Success />);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
it("renders a critical banner", () => {
|
||||
const { container } = render(<Critical />);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
it("renders a banner with an action", () => {
|
||||
const { container } = render(<WithAction />);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
it("renders a banner with an avatar iamge", () => {
|
||||
const { container } = render(<WithAvatarImage />);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
91
packages/shared-components/src/composer/Banner/Banner.tsx
Normal file
91
packages/shared-components/src/composer/Banner/Banner.tsx
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import classNames from "classnames";
|
||||
import React, {
|
||||
type MouseEventHandler,
|
||||
type ReactElement,
|
||||
type ReactNode,
|
||||
type PropsWithChildren,
|
||||
useMemo,
|
||||
} from "react";
|
||||
import { Button } from "@vector-im/compound-web";
|
||||
import CheckCircleIcon from "@vector-im/compound-design-tokens/assets/web/icons/check-circle";
|
||||
import ErrorIcon from "@vector-im/compound-design-tokens/assets/web/icons/error-solid";
|
||||
import InfoIcon from "@vector-im/compound-design-tokens/assets/web/icons/info";
|
||||
|
||||
import styles from "./Banner.module.css";
|
||||
import { _t } from "../../utils/i18n";
|
||||
|
||||
interface BannerProps {
|
||||
/**
|
||||
* The type of the status banner.
|
||||
*/
|
||||
type?: "success" | "info" | "critical";
|
||||
|
||||
/**
|
||||
* The banner avatar.
|
||||
*/
|
||||
avatar?: React.ReactNode;
|
||||
|
||||
className?: string;
|
||||
|
||||
/**
|
||||
* Actions presented to the user in the right-hand side of the banner alongside the dismiss button.
|
||||
*/
|
||||
actions?: ReactNode;
|
||||
/**
|
||||
* Called when the user presses the "dismiss" button.
|
||||
*/
|
||||
onClose: MouseEventHandler<HTMLButtonElement>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A banner component used for displaying user-facing information above the message composer.
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* <Banner onClose={onCloseHandler} />
|
||||
* ```
|
||||
*/
|
||||
export function Banner({
|
||||
type,
|
||||
children,
|
||||
avatar,
|
||||
className,
|
||||
actions,
|
||||
onClose,
|
||||
...props
|
||||
}: PropsWithChildren<BannerProps>): ReactElement {
|
||||
const classes = classNames(styles.banner, className);
|
||||
|
||||
const icon = useMemo(() => {
|
||||
switch (type) {
|
||||
case "critical":
|
||||
return <ErrorIcon fontSize={24} {...props} />;
|
||||
case "info":
|
||||
return <InfoIcon fontSize={24} {...props} />;
|
||||
case "success":
|
||||
return <CheckCircleIcon fontSize={24} {...props} />;
|
||||
default:
|
||||
return <InfoIcon fontSize={24} {...props} />;
|
||||
}
|
||||
}, [type, props]);
|
||||
|
||||
return (
|
||||
<div {...props} className={classes} data-type={type}>
|
||||
<div className={styles.icon}>{avatar ?? icon}</div>
|
||||
<span className={styles.content}>{children}</span>
|
||||
<div className={styles.actions}>
|
||||
{actions}
|
||||
<Button kind="secondary" size="sm" onClick={onClose}>
|
||||
{_t("action|dismiss")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,290 @@
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`AvatarWithDetails renders a banner with an action 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="banner"
|
||||
>
|
||||
<div
|
||||
class="icon"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
font-size="24"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M11.288 7.288A.97.97 0 0 1 12 7q.424 0 .713.287Q13 7.576 13 8t-.287.713A.97.97 0 0 1 12 9a.97.97 0 0 1-.713-.287A.97.97 0 0 1 11 8q0-.424.287-.713m.001 4.001A.97.97 0 0 1 12 11q.424 0 .713.287.287.288.287.713v4q0 .424-.287.712A.97.97 0 0 1 12 17a.97.97 0 0 1-.713-.288A.97.97 0 0 1 11 16v-4q0-.424.287-.713"
|
||||
/>
|
||||
<path
|
||||
clip-rule="evenodd"
|
||||
d="M22 12c0 5.523-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2s10 4.477 10 10m-2 0a8 8 0 1 1-16 0 8 8 0 0 1 16 0"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<span
|
||||
class="content"
|
||||
>
|
||||
<p>
|
||||
encryption|pinned_identity_changed
|
||||
</p>
|
||||
</span>
|
||||
<div
|
||||
class="actions"
|
||||
>
|
||||
<button
|
||||
class="_button_vczzf_8"
|
||||
data-kind="primary"
|
||||
data-size="lg"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
encryption|withdraw_verification_action
|
||||
</button>
|
||||
<button
|
||||
class="_button_vczzf_8"
|
||||
data-kind="secondary"
|
||||
data-size="sm"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Dismiss
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`AvatarWithDetails renders a banner with an avatar iamge 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="banner"
|
||||
>
|
||||
<div
|
||||
class="icon"
|
||||
>
|
||||
<img
|
||||
alt="Example"
|
||||
src="https://picsum.photos/32/32"
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
class="content"
|
||||
>
|
||||
<p>
|
||||
Hello! This is a status banner.
|
||||
</p>
|
||||
</span>
|
||||
<div
|
||||
class="actions"
|
||||
>
|
||||
<button
|
||||
class="_button_vczzf_8"
|
||||
data-kind="secondary"
|
||||
data-size="sm"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Dismiss
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`AvatarWithDetails renders a critical banner 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="banner"
|
||||
data-type="critical"
|
||||
>
|
||||
<div
|
||||
class="icon"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
font-size="24"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 17q.424 0 .713-.288A.97.97 0 0 0 13 16a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 15a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 16q0 .424.287.712.288.288.713.288m0-4q.424 0 .713-.287A.97.97 0 0 0 13 12V8a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 7a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 8v4q0 .424.287.713.288.287.713.287m0 9a9.7 9.7 0 0 1-3.9-.788 10.1 10.1 0 0 1-3.175-2.137q-1.35-1.35-2.137-3.175A9.7 9.7 0 0 1 2 12q0-2.075.788-3.9a10.1 10.1 0 0 1 2.137-3.175q1.35-1.35 3.175-2.137A9.7 9.7 0 0 1 12 2q2.075 0 3.9.788a10.1 10.1 0 0 1 3.175 2.137q1.35 1.35 2.137 3.175A9.7 9.7 0 0 1 22 12a9.7 9.7 0 0 1-.788 3.9 10.1 10.1 0 0 1-2.137 3.175q-1.35 1.35-3.175 2.137A9.7 9.7 0 0 1 12 22"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<span
|
||||
class="content"
|
||||
>
|
||||
<p>
|
||||
Hello! This is a status banner.
|
||||
</p>
|
||||
</span>
|
||||
<div
|
||||
class="actions"
|
||||
>
|
||||
<button
|
||||
class="_button_vczzf_8"
|
||||
data-kind="secondary"
|
||||
data-size="sm"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Dismiss
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`AvatarWithDetails renders a default banner 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="banner"
|
||||
>
|
||||
<div
|
||||
class="icon"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
font-size="24"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M11.288 7.288A.97.97 0 0 1 12 7q.424 0 .713.287Q13 7.576 13 8t-.287.713A.97.97 0 0 1 12 9a.97.97 0 0 1-.713-.287A.97.97 0 0 1 11 8q0-.424.287-.713m.001 4.001A.97.97 0 0 1 12 11q.424 0 .713.287.287.288.287.713v4q0 .424-.287.712A.97.97 0 0 1 12 17a.97.97 0 0 1-.713-.288A.97.97 0 0 1 11 16v-4q0-.424.287-.713"
|
||||
/>
|
||||
<path
|
||||
clip-rule="evenodd"
|
||||
d="M22 12c0 5.523-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2s10 4.477 10 10m-2 0a8 8 0 1 1-16 0 8 8 0 0 1 16 0"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<span
|
||||
class="content"
|
||||
>
|
||||
<p>
|
||||
Hello! This is a status banner.
|
||||
</p>
|
||||
</span>
|
||||
<div
|
||||
class="actions"
|
||||
>
|
||||
<button
|
||||
class="_button_vczzf_8"
|
||||
data-kind="secondary"
|
||||
data-size="sm"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Dismiss
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`AvatarWithDetails renders a info banner 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="banner"
|
||||
data-type="info"
|
||||
>
|
||||
<div
|
||||
class="icon"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
font-size="24"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M11.288 7.288A.97.97 0 0 1 12 7q.424 0 .713.287Q13 7.576 13 8t-.287.713A.97.97 0 0 1 12 9a.97.97 0 0 1-.713-.287A.97.97 0 0 1 11 8q0-.424.287-.713m.001 4.001A.97.97 0 0 1 12 11q.424 0 .713.287.287.288.287.713v4q0 .424-.287.712A.97.97 0 0 1 12 17a.97.97 0 0 1-.713-.288A.97.97 0 0 1 11 16v-4q0-.424.287-.713"
|
||||
/>
|
||||
<path
|
||||
clip-rule="evenodd"
|
||||
d="M22 12c0 5.523-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2s10 4.477 10 10m-2 0a8 8 0 1 1-16 0 8 8 0 0 1 16 0"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<span
|
||||
class="content"
|
||||
>
|
||||
<p>
|
||||
Hello! This is a status banner.
|
||||
</p>
|
||||
</span>
|
||||
<div
|
||||
class="actions"
|
||||
>
|
||||
<button
|
||||
class="_button_vczzf_8"
|
||||
data-kind="secondary"
|
||||
data-size="sm"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Dismiss
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`AvatarWithDetails renders a success banner 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="banner"
|
||||
data-type="success"
|
||||
>
|
||||
<div
|
||||
class="icon"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
font-size="24"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="m10.6 13.8-2.15-2.15a.95.95 0 0 0-.7-.275.95.95 0 0 0-.7.275.95.95 0 0 0-.275.7q0 .425.275.7L9.9 15.9q.3.3.7.3t.7-.3l5.65-5.65a.95.95 0 0 0 .275-.7.95.95 0 0 0-.275-.7.95.95 0 0 0-.7-.275.95.95 0 0 0-.7.275zM12 22a9.7 9.7 0 0 1-3.9-.788 10.1 10.1 0 0 1-3.175-2.137q-1.35-1.35-2.137-3.175A9.7 9.7 0 0 1 2 12q0-2.075.788-3.9a10.1 10.1 0 0 1 2.137-3.175q1.35-1.35 3.175-2.137A9.7 9.7 0 0 1 12 2q2.075 0 3.9.788a10.1 10.1 0 0 1 3.175 2.137q1.35 1.35 2.137 3.175A9.7 9.7 0 0 1 22 12a9.7 9.7 0 0 1-.788 3.9 10.1 10.1 0 0 1-2.137 3.175q-1.35 1.35-3.175 2.137A9.7 9.7 0 0 1 12 22m0-2q3.35 0 5.675-2.325T20 12t-2.325-5.675T12 4 6.325 6.325 4 12t2.325 5.675T12 20"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<span
|
||||
class="content"
|
||||
>
|
||||
<p>
|
||||
Hello! This is a status banner.
|
||||
</p>
|
||||
</span>
|
||||
<div
|
||||
class="actions"
|
||||
>
|
||||
<button
|
||||
class="_button_vczzf_8"
|
||||
data-kind="secondary"
|
||||
data-size="sm"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Dismiss
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
8
packages/shared-components/src/composer/Banner/index.ts
Normal file
8
packages/shared-components/src/composer/Banner/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
export * from "./Banner";
|
||||
@@ -9,7 +9,7 @@ import React from "react";
|
||||
import { type Meta, type StoryFn } from "@storybook/react-vite";
|
||||
|
||||
import { TextualEventView as TextualEventComponent } from "./TextualEventView";
|
||||
import { MockViewModel } from "../../MockViewModel";
|
||||
import { MockViewModel } from "../../viewmodel/MockViewModel";
|
||||
|
||||
export default {
|
||||
title: "Event/TextualEvent",
|
||||
|
||||
@@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
|
||||
import React, { type ReactNode, type JSX } from "react";
|
||||
|
||||
import { type ViewModel } from "../../ViewModel";
|
||||
import { type ViewModel } from "../../viewmodel/ViewModel";
|
||||
import { useViewModel } from "../../useViewModel";
|
||||
|
||||
export type TextualEventViewSnapshot = {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`TextualEventView renders a textual event 1`] = `
|
||||
<div>
|
||||
|
||||
@@ -5,4 +5,4 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
export { TextualEventView } from "./TextualEventView";
|
||||
export { TextualEventView, type TextualEventViewSnapshot } from "./TextualEventView";
|
||||
|
||||
38
packages/shared-components/src/index.ts
Normal file
38
packages/shared-components/src/index.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// Components
|
||||
export * from "./audio/AudioPlayerView";
|
||||
export * from "./audio/Clock";
|
||||
export * from "./audio/PlayPauseButton";
|
||||
export * from "./audio/SeekBar";
|
||||
export * from "./avatar/AvatarWithDetails";
|
||||
export * from "./composer/Banner";
|
||||
export * from "./event-tiles/TextualEventView";
|
||||
export * from "./message-body/MediaBody";
|
||||
export * from "./pill-input/Pill";
|
||||
export * from "./pill-input/PillInput";
|
||||
export * from "./rich-list/RichItem";
|
||||
export * from "./rich-list/RichList";
|
||||
export * from "./utils/Box";
|
||||
export * from "./utils/Flex";
|
||||
|
||||
// Utils
|
||||
export * from "./utils/i18n";
|
||||
export * from "./utils/humanize";
|
||||
export * from "./utils/DateUtils";
|
||||
export * from "./utils/numbers";
|
||||
export * from "./utils/FormattingUtils";
|
||||
|
||||
// MVVM
|
||||
export * from "./viewmodel";
|
||||
export * from "./useMockedViewModel";
|
||||
export * from "./useViewModel";
|
||||
|
||||
// i18n (we must export this directly in order to not confuse the type bundler, it seems,
|
||||
// otherwise it will leave it as a relative import rather than bundling it)
|
||||
export type * from "./i18nKeys.d.ts";
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
.mediaBody {
|
||||
background-color: var(--cpd-color-bg-subtle-secondary);
|
||||
border-radius: var(--cpd-space-2x);
|
||||
border-radius: var(--cpd-space-2x) !important;
|
||||
max-width: 243px; /* use max-width instead of width so it fits within right panels */
|
||||
|
||||
font: var(--cpd-font-body-md-regular);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`MediaBody renders the media body 1`] = `
|
||||
<div>
|
||||
|
||||
@@ -53,7 +53,13 @@ export function Pill({ className, children, label, onClick, ...props }: PropsWit
|
||||
{label}
|
||||
</span>
|
||||
{onClick && (
|
||||
<IconButton aria-describedby={id} size="16px" onClick={onClick} aria-label={_t("action|delete")}>
|
||||
<IconButton
|
||||
aria-describedby={id}
|
||||
size="16px"
|
||||
onClick={onClick}
|
||||
aria-label={_t("action|delete")}
|
||||
className="mx_Dialog_nonDialogButton"
|
||||
>
|
||||
<CloseIcon color="var(--cpd-color-icon-tertiary)" />
|
||||
</IconButton>
|
||||
)}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`Pill renders the pill 1`] = `
|
||||
<div>
|
||||
@@ -11,14 +11,14 @@ exports[`Pill renders the pill 1`] = `
|
||||
/>
|
||||
<span
|
||||
class="label"
|
||||
id="«r0»"
|
||||
id="_r_0_"
|
||||
>
|
||||
Pill
|
||||
</span>
|
||||
<button
|
||||
aria-describedby="«r0»"
|
||||
aria-describedby="_r_0_"
|
||||
aria-label="Delete"
|
||||
class="_icon-button_1pz9o_8"
|
||||
class="_icon-button_1pz9o_8 mx_Dialog_nonDialogButton"
|
||||
data-kind="primary"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 16px;"
|
||||
@@ -57,7 +57,7 @@ exports[`Pill renders the pill without close button 1`] = `
|
||||
/>
|
||||
<span
|
||||
class="label"
|
||||
id="«r1»"
|
||||
id="_r_1_"
|
||||
>
|
||||
Pill
|
||||
</span>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`PillInput renders only the input without children 1`] = `
|
||||
<div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`RichItem renders the item in default state 1`] = `
|
||||
<div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`RichItem renders the list 1`] = `
|
||||
<div>
|
||||
@@ -11,12 +11,12 @@ exports[`RichItem renders the list 1`] = `
|
||||
>
|
||||
<span
|
||||
class="title"
|
||||
id="«r0»"
|
||||
id="_r_0_"
|
||||
>
|
||||
Rich List Title
|
||||
</span>
|
||||
<ul
|
||||
aria-labelledby="«r0»"
|
||||
aria-labelledby="_r_0_"
|
||||
class="content"
|
||||
role="listbox"
|
||||
tabindex="0"
|
||||
@@ -174,7 +174,7 @@ exports[`RichItem renders the list with isEmpty=true 1`] = `
|
||||
>
|
||||
<span
|
||||
class="title"
|
||||
id="«r1»"
|
||||
id="_r_1_"
|
||||
>
|
||||
Rich List Title
|
||||
</span>
|
||||
|
||||
22
packages/shared-components/src/test/setupTests.ts
Normal file
22
packages/shared-components/src/test/setupTests.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
Copyright 2025 Element Creations Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import fetchMock from "fetch-mock-jest";
|
||||
|
||||
import { setLanguage } from "../../src/utils/i18n";
|
||||
import en from "../../../../src/i18n/strings/en_EN.json";
|
||||
|
||||
export function setupLanguageMock(): void {
|
||||
fetchMock
|
||||
.get("/i18n/languages.json", {
|
||||
en: "en_EN.json",
|
||||
})
|
||||
.get("end:en_EN.json", en);
|
||||
}
|
||||
setupLanguageMock();
|
||||
|
||||
setLanguage("en");
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
Copyright 2025 Element Creations Ltd.
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
// Copied from element-web/test/test-utils because, seemingly, if we
|
||||
// set that as the modules directory to use it directly, it fails to
|
||||
// actually put the right thing in the context somehow.
|
||||
|
||||
import React, { type ReactElement } from "react";
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { render, type RenderOptions } from "@testing-library/react";
|
||||
import { TooltipProvider } from "@vector-im/compound-web";
|
||||
|
||||
const wrapWithTooltipProvider = (Wrapper: RenderOptions["wrapper"]) => {
|
||||
return ({ children }: { children: React.ReactNode }) => {
|
||||
if (Wrapper) {
|
||||
return (
|
||||
<Wrapper>
|
||||
<TooltipProvider>{children}</TooltipProvider>
|
||||
</Wrapper>
|
||||
);
|
||||
} else {
|
||||
return <TooltipProvider>{children}</TooltipProvider>;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const customRender = (ui: ReactElement, options: RenderOptions = {}): ReturnType<typeof render> => {
|
||||
return render(ui, {
|
||||
...options,
|
||||
wrapper: wrapWithTooltipProvider(options?.wrapper) as RenderOptions["wrapper"],
|
||||
}) as ReturnType<typeof render>;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
export * from "@testing-library/react";
|
||||
|
||||
/**
|
||||
* This custom render function wraps your component with a TooltipProvider.
|
||||
* See https://testing-library.com/docs/react-testing-library/setup/#custom-render
|
||||
*/
|
||||
export { customRender as render };
|
||||
25
packages/shared-components/src/useMockedViewModel.ts
Normal file
25
packages/shared-components/src/useMockedViewModel.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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 { useMemo } from "react";
|
||||
|
||||
import { MockViewModel, type ViewModel } from "./viewmodel";
|
||||
|
||||
/**
|
||||
* Hook helper to return a mocked view model created with the given snapshot and actions.
|
||||
* This is useful for testing components in isolation with a mocked view model and allows to use primitive types in stories.
|
||||
*
|
||||
* @param snapshot
|
||||
* @param actions
|
||||
*/
|
||||
export function useMockedViewModel<S, A>(snapshot: S, actions: A): ViewModel<S> & A {
|
||||
return useMemo(() => {
|
||||
const vm = new MockViewModel<S>(snapshot);
|
||||
Object.assign(vm, actions);
|
||||
return vm as unknown as ViewModel<S> & A;
|
||||
}, [snapshot, actions]);
|
||||
}
|
||||
@@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
|
||||
import { useSyncExternalStore } from "react";
|
||||
|
||||
import { type ViewModel } from "./ViewModel";
|
||||
import { type ViewModel } from "./viewmodel/ViewModel";
|
||||
|
||||
/**
|
||||
* A small wrapper around useSyncExternalStore to use a view model in a shared component view
|
||||
|
||||
46
packages/shared-components/src/utils/i18n.test.ts
Normal file
46
packages/shared-components/src/utils/i18n.test.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2025 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import counterpart from "counterpart";
|
||||
|
||||
import { registerTranslations, setMissingEntryGenerator, getLocale, setLocale } from "./i18n";
|
||||
|
||||
describe("i18n utils", () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it("should wrap registerTranslations", () => {
|
||||
jest.spyOn(counterpart, "registerTranslations");
|
||||
|
||||
registerTranslations("en", { test: "This is a test" });
|
||||
expect(counterpart.registerTranslations).toHaveBeenCalledWith("en", { test: "This is a test" });
|
||||
});
|
||||
|
||||
it("should wrap setMissingEntryGenerator", () => {
|
||||
jest.spyOn(counterpart, "setMissingEntryGenerator");
|
||||
|
||||
const dummyFn = jest.fn();
|
||||
|
||||
setMissingEntryGenerator(dummyFn);
|
||||
expect(counterpart.setMissingEntryGenerator).toHaveBeenCalledWith(dummyFn);
|
||||
});
|
||||
|
||||
it("should wrap getLocale", () => {
|
||||
jest.spyOn(counterpart, "getLocale");
|
||||
|
||||
getLocale();
|
||||
expect(counterpart.getLocale).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should wrap setLocale", () => {
|
||||
jest.spyOn(counterpart, "setLocale");
|
||||
|
||||
setLocale("en");
|
||||
expect(counterpart.setLocale).toHaveBeenCalledWith("en");
|
||||
});
|
||||
});
|
||||
@@ -22,10 +22,10 @@
|
||||
* @return a React <span> component if any non-strings were used in substitutions, otherwise a string
|
||||
*/
|
||||
import React from "react";
|
||||
import { type TranslationKey as _TranslationKey, KEY_SEPARATOR } from "matrix-web-i18n";
|
||||
import { KEY_SEPARATOR } from "matrix-web-i18n";
|
||||
import counterpart from "counterpart";
|
||||
|
||||
import type Translations from "../../../../src/i18n/strings/en_EN.json";
|
||||
import type { TranslationKey } from "../index";
|
||||
|
||||
// @ts-ignore - $webapp is a webpack resolve alias pointing to the output directory, see webpack config
|
||||
import webpackLangJsonUrl from "$webapp/i18n/languages.json";
|
||||
@@ -45,16 +45,23 @@ counterpart.setSeparator(KEY_SEPARATOR);
|
||||
const FALLBACK_LOCALE = "en";
|
||||
counterpart.setFallbackLocale(FALLBACK_LOCALE);
|
||||
|
||||
/**
|
||||
* A type representing the union of possible keys into the translation file using `|` delimiter to access nested fields.
|
||||
* @example `common|error` to access `error` within the `common` sub-object.
|
||||
* {
|
||||
* "common": {
|
||||
* "error": "Error"
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
export type TranslationKey = _TranslationKey<typeof Translations>;
|
||||
// export wrappers around these functions because if we used counterpart directly from
|
||||
// element-web, it operates on a different instance of counterpart
|
||||
export function registerTranslations(locale: string, data: object): void {
|
||||
counterpart.registerTranslations(locale, data);
|
||||
}
|
||||
|
||||
export function setMissingEntryGenerator(callback: (value: string) => void): void {
|
||||
counterpart.setMissingEntryGenerator(callback);
|
||||
}
|
||||
|
||||
export function getLocale(): string {
|
||||
return counterpart.getLocale();
|
||||
}
|
||||
|
||||
export function setLocale(value: string): string {
|
||||
return counterpart.setLocale(value);
|
||||
}
|
||||
|
||||
// Function which only purpose is to mark that a string is translatable
|
||||
// Does not actually do anything. It's helpful for automatic extraction of translatable strings
|
||||
|
||||
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { type ViewModel } from "../../../packages/shared-components/src/ViewModel";
|
||||
import { type ViewModel } from "./ViewModel";
|
||||
import { Disposables } from "./Disposables";
|
||||
import { Snapshot } from "./Snapshot";
|
||||
import { ViewModelSubscriptions } from "./ViewModelSubscriptions";
|
||||
13
packages/shared-components/src/viewmodel/index.ts
Normal file
13
packages/shared-components/src/viewmodel/index.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export * from "./BaseViewModel";
|
||||
export * from "./Disposables";
|
||||
export * from "./Snapshot";
|
||||
export * from "./ViewModelSubscriptions";
|
||||
export type * from "./ViewModel";
|
||||
export * from "./MockViewModel";
|
||||
@@ -6,7 +6,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
|
||||
import { EventEmitter } from "events";
|
||||
|
||||
import { Disposables } from "../../../src/viewmodels/base/Disposables";
|
||||
import { Disposables } from "..";
|
||||
|
||||
describe("Disposable", () => {
|
||||
it("isDisposed is true after dispose() is called", () => {
|
||||
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { Snapshot } from "../../../src/viewmodels/base/Snapshot";
|
||||
import { Snapshot } from "..";
|
||||
|
||||
interface TestSnapshot {
|
||||
key1: string;
|
||||
@@ -7,7 +7,7 @@
|
||||
"esModuleInterop": true,
|
||||
"useDefineForClassFields": true,
|
||||
"module": "es2022",
|
||||
"moduleResolution": "node",
|
||||
"moduleResolution": "bundler",
|
||||
"target": "es2022",
|
||||
"noUnusedLocals": true,
|
||||
"sourceMap": false,
|
||||
@@ -17,15 +17,9 @@
|
||||
"lib": ["es2022", "es2024.promise", "dom", "dom.iterable"],
|
||||
"strict": true,
|
||||
"paths": {
|
||||
"jest-matrix-react": ["../../test/test-utils/jest-matrix-react"],
|
||||
"jest-matrix-react": ["./src/test/utils/jest-matrix-react"],
|
||||
"rollup/parseAst": ["./node_modules/rollup/dist/parseAst"]
|
||||
}
|
||||
},
|
||||
"include": ["./src/**/*.ts", "./src/**/*.tsx"],
|
||||
"ts-node": {
|
||||
"files": true,
|
||||
"moduleTypes": {
|
||||
"*": "cjs"
|
||||
}
|
||||
}
|
||||
"include": ["./src/**/*.ts", "./src/**/*.tsx"]
|
||||
}
|
||||
|
||||
54
packages/shared-components/vite.config.js
Normal file
54
packages/shared-components/vite.config.js
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
*
|
||||
* 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 { dirname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { defineConfig } from "vite";
|
||||
import dts from "vite-plugin-dts";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
export default defineConfig({
|
||||
build: {
|
||||
lib: {
|
||||
entry: resolve(__dirname, "src/index.ts"),
|
||||
name: "Element Web Shared Components",
|
||||
// the proper extensions will be added
|
||||
fileName: "element-web-shared-components",
|
||||
},
|
||||
outDir: "dist",
|
||||
rollupOptions: {
|
||||
// make sure to externalize deps that shouldn't be bundled
|
||||
// into your library
|
||||
external: ["react", "react-dom", "@vector-im/compound-design-tokens", "@vector-im/compound-web"],
|
||||
output: {
|
||||
// Provide global variables to use in the UMD build
|
||||
// for externalized deps
|
||||
globals: {
|
||||
"react": "react",
|
||||
"react-dom": "ReactDom",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
// Alias used by i18n.tsx
|
||||
$webapp: resolve(__dirname, "..", "..", "webapp"),
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
dts({
|
||||
rollupTypes: true,
|
||||
include: ["src/**/*.{ts,tsx}"],
|
||||
exclude: ["src/**/*.test.{ts,tsx}"],
|
||||
copyDtsFiles: true,
|
||||
}),
|
||||
],
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
56
patches/jsdom+26.1.0.patch
Normal file
56
patches/jsdom+26.1.0.patch
Normal file
@@ -0,0 +1,56 @@
|
||||
diff --git a/node_modules/jsdom/lib/jsdom/browser/Window.js b/node_modules/jsdom/lib/jsdom/browser/Window.js
|
||||
index 52d011c..f62f6d6 100644
|
||||
--- a/node_modules/jsdom/lib/jsdom/browser/Window.js
|
||||
+++ b/node_modules/jsdom/lib/jsdom/browser/Window.js
|
||||
@@ -505,10 +505,10 @@ function installOwnProperties(window, options) {
|
||||
event: makeReplaceablePropertyDescriptor("event", window),
|
||||
|
||||
// [LegacyUnforgeable]:
|
||||
- window: { configurable: false },
|
||||
- document: { configurable: false },
|
||||
- location: { configurable: false },
|
||||
- top: { configurable: false }
|
||||
+ window: { configurable: true },
|
||||
+ document: { configurable: true },
|
||||
+ location: { configurable: true },
|
||||
+ top: { configurable: true }
|
||||
});
|
||||
|
||||
|
||||
diff --git a/node_modules/jsdom/lib/jsdom/living/generated/Location.js b/node_modules/jsdom/lib/jsdom/living/generated/Location.js
|
||||
index fc4d1dd..c855bd5 100644
|
||||
--- a/node_modules/jsdom/lib/jsdom/living/generated/Location.js
|
||||
+++ b/node_modules/jsdom/lib/jsdom/living/generated/Location.js
|
||||
@@ -322,19 +322,19 @@ function getUnforgeables(globalObject) {
|
||||
}
|
||||
});
|
||||
Object.defineProperties(unforgeables, {
|
||||
- assign: { configurable: false, writable: false },
|
||||
- replace: { configurable: false, writable: false },
|
||||
- reload: { configurable: false, writable: false },
|
||||
- href: { configurable: false },
|
||||
- toString: { configurable: false, writable: false },
|
||||
- origin: { configurable: false },
|
||||
- protocol: { configurable: false },
|
||||
- host: { configurable: false },
|
||||
- hostname: { configurable: false },
|
||||
- port: { configurable: false },
|
||||
- pathname: { configurable: false },
|
||||
- search: { configurable: false },
|
||||
- hash: { configurable: false }
|
||||
+ assign: { configurable: true, writable: false },
|
||||
+ replace: { configurable: true, writable: false },
|
||||
+ reload: { configurable: true, writable: false },
|
||||
+ href: { configurable: true },
|
||||
+ toString: { configurable: true, writable: false },
|
||||
+ origin: { configurable: true },
|
||||
+ protocol: { configurable: true },
|
||||
+ host: { configurable: true },
|
||||
+ hostname: { configurable: true },
|
||||
+ port: { configurable: true },
|
||||
+ pathname: { configurable: true },
|
||||
+ search: { configurable: true },
|
||||
+ hash: { configurable: true }
|
||||
});
|
||||
unforgeablesMap.set(globalObject, unforgeables);
|
||||
}
|
||||
@@ -49,7 +49,7 @@ test.describe("Audio player", { tag: ["@no-firefox", "@no-webkit"] }, () => {
|
||||
/**
|
||||
* Take snapshots of mx_EventTile_last on each layout, outputting log for reference/debugging.
|
||||
* @param detail The snapshot name. Used for outputting logs too.
|
||||
* @param monospace This changes the font used to render the UI from a default one to Inconsolata. Set to false by default.
|
||||
* @param monospace This changes the font used to render the UI from a default one to Fira Code. Set to false by default.
|
||||
*/
|
||||
const takeSnapshots = async (page: Page, app: ElementAppPage, detail: string, monospace = false) => {
|
||||
// Check that the audio player is rendered and its button becomes visible
|
||||
@@ -65,7 +65,7 @@ test.describe("Audio player", { tag: ["@no-firefox", "@no-webkit"] }, () => {
|
||||
|
||||
if (monospace) {
|
||||
// Assert that the monospace timer is visible
|
||||
await expect(locator.locator("[role='timer']")).toHaveCSS("font-family", "Inconsolata");
|
||||
await expect(locator.locator("[role='timer']")).toHaveCSS("font-family", '"Fira Code"');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -73,7 +73,7 @@ test.describe("Audio player", { tag: ["@no-firefox", "@no-webkit"] }, () => {
|
||||
// Enable system font and monospace setting
|
||||
await app.settings.setValue("useBundledEmojiFont", null, SettingLevel.DEVICE, false);
|
||||
await app.settings.setValue("useSystemFont", null, SettingLevel.DEVICE, true);
|
||||
await app.settings.setValue("systemFont", null, SettingLevel.DEVICE, "Inconsolata");
|
||||
await app.settings.setValue("systemFont", null, SettingLevel.DEVICE, "Fira Code");
|
||||
}
|
||||
|
||||
// Check the status of the seek bar
|
||||
@@ -92,7 +92,7 @@ test.describe("Audio player", { tag: ["@no-firefox", "@no-webkit"] }, () => {
|
||||
css: `
|
||||
/* The timestamp is of inconsistent width depending on the time the test runs at */
|
||||
.mx_MessageTimestamp {
|
||||
display: none !important;
|
||||
visibility: hidden;
|
||||
}
|
||||
/* The MAB showing up on hover is not needed for the test */
|
||||
.mx_MessageActionBar {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user