diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml index 35b8f86cab..6b00899cf0 100644 --- a/.github/workflows/style.yml +++ b/.github/workflows/style.yml @@ -110,6 +110,8 @@ jobs: - name: Web tsslint if: steps.changed-files.outputs.any_changed == 'true' working-directory: ./web + env: + NODE_OPTIONS: --max-old-space-size=4096 run: vp run lint:tss - name: Web type check diff --git a/api/pyproject.toml b/api/pyproject.toml index 834fdfc68e..2587d9e0bf 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -6,7 +6,7 @@ requires-python = "~=3.12.0" dependencies = [ # Legacy: mature and widely deployed "bleach>=6.3.0", - "boto3>=1.42.91", + "boto3>=1.42.96", "celery>=5.6.3", "croniter>=6.2.2", "flask>=3.1.3,<4.0.0", @@ -185,12 +185,12 @@ dev = [ storage = [ "azure-storage-blob>=12.28.0", "bce-python-sdk>=0.9.70", - "cos-python-sdk-v5>=1.9.41", + "cos-python-sdk-v5>=1.9.42", "esdk-obs-python>=3.22.2", "google-cloud-storage>=3.10.1", "opendal>=0.46.0", "oss2>=2.19.1", - "supabase>=2.28.3", + "supabase>=2.29.0", "tos>=2.9.0", ] diff --git a/api/uv.lock b/api/uv.lock index 18a736d4b7..1b52f8b53f 100644 --- a/api/uv.lock +++ b/api/uv.lock @@ -604,16 +604,16 @@ wheels = [ [[package]] name = "boto3" -version = "1.42.91" +version = "1.42.96" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "botocore" }, { name = "jmespath" }, { name = "s3transfer" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a7/c0/98b8cec7ca22dde776df48c58940ae1abc425593959b7226e270760d726f/boto3-1.42.91.tar.gz", hash = "sha256:03d70532b17f7f84df37ca7e8c21553280454dea53ae12b15d1cfef9b16fcb8a", size = 113181, upload-time = "2026-04-17T19:31:06.251Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a6/2d/69fb3acd50bab83fb295c167d33c4b653faeb5fb0f42bfca4d9b69d6fb68/boto3-1.42.96.tar.gz", hash = "sha256:b38a9e4a3fbbee9017252576f1379780d0a5814768676c08df2f539d31fcdd68", size = 113203, upload-time = "2026-04-24T19:47:18.677Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/02/29/faba6521257c34085cc9b439ef98235b581772580f417fa3629728007270/boto3-1.42.91-py3-none-any.whl", hash = "sha256:04e72071cde022951ce7f81bd9933c90095ab8923e8ced61c8dacfe9edac0f5c", size = 140553, upload-time = "2026-04-17T19:31:02.57Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9d/b3f617d011c42eb804d993103b8fa9acdce153e181a3042f58bfe33d7cb4/boto3-1.42.96-py3-none-any.whl", hash = "sha256:2f4566da2c209a98bdbfc874d813ef231c84ad24e4f815e9bc91de5f63351a24", size = 140557, upload-time = "2026-04-24T19:47:15.824Z" }, ] [[package]] @@ -636,16 +636,16 @@ bedrock-runtime = [ [[package]] name = "botocore" -version = "1.42.91" +version = "1.42.96" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jmespath" }, { name = "python-dateutil" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/21/bc/a4b7c46471c2e789ad8c4c7acfd7f302fdb481d93ff870f441249b924ae6/botocore-1.42.91.tar.gz", hash = "sha256:d252e27bc454afdbf5ed3dc617aa423f2c855c081e98b7963093399483ecc698", size = 15213010, upload-time = "2026-04-17T19:30:50.793Z" } +sdist = { url = "https://files.pythonhosted.org/packages/61/77/2c333622a1d47cf5bf73cdcab0cb6c92addafbef2ec05f81b9f75687d9e5/botocore-1.42.96.tar.gz", hash = "sha256:75b3b841ffacaa944f645196655a21ca777591dd8911e732bfb6614545af0250", size = 15263344, upload-time = "2026-04-24T19:47:05.283Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/fc/24cc0a47c824f13933e210e9ad034b4fba22f7185b8d904c0fbf5a3b2be8/botocore-1.42.91-py3-none-any.whl", hash = "sha256:7a28c3cc6bfab5724ad18899d52402b776a0de7d87fa20c3c5270bcaaf199ce8", size = 14897344, upload-time = "2026-04-17T19:30:44.245Z" }, + { url = "https://files.pythonhosted.org/packages/45/56/152c3a859ca1b9d77ed16deac3cf81682013677c68cf5715698781fc81bd/botocore-1.42.96-py3-none-any.whl", hash = "sha256:db2c3e2006628be6fde81a24124a6563c363d6982fb92728837cf174bad9d98a", size = 14945920, upload-time = "2026-04-24T19:47:00.323Z" }, ] [[package]] @@ -1058,7 +1058,7 @@ wheels = [ [[package]] name = "cos-python-sdk-v5" -version = "1.9.41" +version = "1.9.42" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "crcmod" }, @@ -1067,9 +1067,9 @@ dependencies = [ { name = "six" }, { name = "xmltodict" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0e/38/c0029f413f51238aa2319715f45d74bcae931768e36c7e4604b02f407c6c/cos_python_sdk_v5-1.9.41.tar.gz", hash = "sha256:68f4be7d8fe27a1d186b3159b93c622816e398effdc236eddd442b86db592b82", size = 102625, upload-time = "2026-01-06T07:00:11.692Z" } +sdist = { url = "https://files.pythonhosted.org/packages/40/e3/b903b4acde334510f481d126a686bc4013710c00e2af34bff369511329ac/cos_python_sdk_v5-1.9.42.tar.gz", hash = "sha256:2a01d1868f50c5a70771f2b67da868f1dc6c6f3890f8009715313834404decc4", size = 102670, upload-time = "2026-04-23T11:08:27.949Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/aa/2f/ead3fb551509fdc94e4a42093b770e3de2827ff7227570165df5e35c2a3e/cos_python_sdk_v5-1.9.41-py3-none-any.whl", hash = "sha256:f465aae43a4ba3f1caa8caeaca838d0395932f6848e89d6dde2807725e3c88a0", size = 98285, upload-time = "2026-01-06T06:43:02.754Z" }, + { url = "https://files.pythonhosted.org/packages/ee/bf/4ea660bb79d91fd41ba394605eccffd3d0943ed547b3fe2bdc6c7a52d2d1/cos_python_sdk_v5-1.9.42-py3-none-any.whl", hash = "sha256:02e583a1094e1794e6c0f56618d5190eb9eb7bfe75909f1dfac41bbee46e46c5", size = 98375, upload-time = "2026-04-23T11:05:14.519Z" }, ] [[package]] @@ -1578,7 +1578,7 @@ requires-dist = [ { name = "aliyun-log-python-sdk", specifier = ">=0.9.44,<1.0.0" }, { name = "azure-identity", specifier = ">=1.25.3,<2.0.0" }, { name = "bleach", specifier = ">=6.3.0" }, - { name = "boto3", specifier = ">=1.42.91" }, + { name = "boto3", specifier = ">=1.42.96" }, { name = "celery", specifier = ">=5.6.3" }, { name = "croniter", specifier = ">=6.2.2" }, { name = "fastopenapi", extras = ["flask"], specifier = "~=0.7.0" }, @@ -1684,12 +1684,12 @@ dev = [ storage = [ { name = "azure-storage-blob", specifier = ">=12.28.0" }, { name = "bce-python-sdk", specifier = ">=0.9.70" }, - { name = "cos-python-sdk-v5", specifier = ">=1.9.41" }, + { name = "cos-python-sdk-v5", specifier = ">=1.9.42" }, { name = "esdk-obs-python", specifier = ">=3.22.2" }, { name = "google-cloud-storage", specifier = ">=3.10.1" }, { name = "opendal", specifier = ">=0.46.0" }, { name = "oss2", specifier = ">=2.19.1" }, - { name = "supabase", specifier = ">=2.28.3" }, + { name = "supabase", specifier = ">=2.29.0" }, { name = "tos", specifier = ">=2.9.0" }, ] tools = [ @@ -4810,7 +4810,7 @@ wheels = [ [[package]] name = "postgrest" -version = "2.28.3" +version = "2.29.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "deprecation" }, @@ -4818,9 +4818,9 @@ dependencies = [ { name = "pydantic" }, { name = "yarl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/40/60/9378ddd6e21b6005b34aeb42dc7a9ed9985c673c97c9b6a1858f9c52ebbd/postgrest-2.28.3.tar.gz", hash = "sha256:56336e9304950a78315ec7d6c8eb307cdb964d0878a7bec6111392ddb6c16a45", size = 13758, upload-time = "2026-03-20T14:38:06.542Z" } +sdist = { url = "https://files.pythonhosted.org/packages/52/98/f216b8b5c4d116ab6a2fb21339b5821da279ee773e163612418e1c56c012/postgrest-2.29.0.tar.gz", hash = "sha256:a87081858f627fcd57e8e7137004a1ef0adbdf0dbdfed1384e9ea1d7a9c525ec", size = 14217, upload-time = "2026-04-24T13:13:00.281Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/5e/6eeb1d53d010d80e800204c1eee6b3d5419a6a2b985c364f56f36cf48cca/postgrest-2.28.3-py3-none-any.whl", hash = "sha256:5a44d6c6d509abdbe0f928c86f0dc31ef26bda36e0357129836ec54dfb50b083", size = 21865, upload-time = "2026-03-20T14:38:05.55Z" }, + { url = "https://files.pythonhosted.org/packages/2c/0b/08b670a93a90d625c557b9e64b8a5fdeec80c3542d2d0265f0b4d6b16646/postgrest-2.29.0-py3-none-any.whl", hash = "sha256:3ee48e146f726272733d20e2b12de354cdb6cb9dd9cc3a61ed97ce69047aeb96", size = 22735, upload-time = "2026-04-24T13:12:58.405Z" }, ] [[package]] @@ -5723,16 +5723,16 @@ wheels = [ [[package]] name = "realtime" -version = "2.28.3" +version = "2.29.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "typing-extensions" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9c/3d/ef6ed9221f98766f3a503e6e3ac68fa7ca25c117b383f1efc448294232ac/realtime-2.28.3.tar.gz", hash = "sha256:5cc83a6217874426799d8bf74e96d904ac6fa77c39fa8982fa99287947eb2cbf", size = 18723, upload-time = "2026-03-20T14:38:08.424Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6e/f1/08c42a42653942fadfbef495d5b0239356140e7186cc528704956c5f06d4/realtime-2.29.0.tar.gz", hash = "sha256:8efe4a1b3a548a5fda09de701bd041fa0970c5a2fe7d13db0b9861ce11828be2", size = 18715, upload-time = "2026-04-24T13:13:02.315Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/d5/659405f9d4c9b022b7ac02bd52986ccc081f211db081051440f46bf4f358/realtime-2.28.3-py3-none-any.whl", hash = "sha256:efe484d6d39024c7e00ef70f70be600142e9407e5d802de8c96e86e014ce3b36", size = 22378, upload-time = "2026-03-20T14:38:07.144Z" }, + { url = "https://files.pythonhosted.org/packages/77/48/f6375c0a24923beb988f0c71c052604c96641cf43c2d22b91ec1df86afa0/realtime-2.29.0-py3-none-any.whl", hash = "sha256:1a4891e6c82e88ac9d96ac715e435e086f6f8c7665212a8717346de829cbb509", size = 22374, upload-time = "2026-04-24T13:13:01.103Z" }, ] [[package]] @@ -6214,7 +6214,7 @@ wheels = [ [[package]] name = "storage3" -version = "2.28.3" +version = "2.29.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "deprecation" }, @@ -6223,9 +6223,9 @@ dependencies = [ { name = "pyiceberg" }, { name = "yarl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/eb/b5/18df59ba92951d74774eb0265072bf236ead5e3cbc4b802d8bf1cf3581a0/storage3-2.28.3.tar.gz", hash = "sha256:2b3f843cbd44c4a3b483ec076a12c27de88c0ad5358a43067ed44ef08292353f", size = 20109, upload-time = "2026-03-20T14:38:11.467Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/be/771246434b5caf3c6187bfdc932eaede00bf5f2937b47475ab25209ede3e/storage3-2.29.0.tar.gz", hash = "sha256:b0cc2f6714655d725c998d2c5ae8c6fb4f56a513bd31e4f85770df557fe021e3", size = 20160, upload-time = "2026-04-24T13:13:04.626Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ad/a5/2dbe216954e026a8c2e2dc7dfa5fd7b1a1ae0824d10972e62462f4f15aca/storage3-2.28.3-py3-none-any.whl", hash = "sha256:bac35c5087619174448fdef6a337db4e3dfebf3de69f685bd706de93ddcdad69", size = 28239, upload-time = "2026-03-20T14:38:10.423Z" }, + { url = "https://files.pythonhosted.org/packages/fc/c3/790c31866f52c13b26f108b45759bf50dafae3a0bafb4511fadc98ba7c33/storage3-2.29.0-py3-none-any.whl", hash = "sha256:043ef7ff27cc8b9da12be403cf78ee4586180edfcf62b227ff61e1bd79594b06", size = 28284, upload-time = "2026-04-24T13:13:03.338Z" }, ] [[package]] @@ -6251,7 +6251,7 @@ wheels = [ [[package]] name = "supabase" -version = "2.28.3" +version = "2.29.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, @@ -6262,37 +6262,37 @@ dependencies = [ { name = "supabase-functions" }, { name = "yarl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5a/98/2f1c95a2269ce995a34f275760b1c2ee71ee7a75649238ca0470afdfc2ef/supabase-2.28.3.tar.gz", hash = "sha256:1200961e46cdec17c7c280a1e09a159544643eada2759591ea69835303a2e1a4", size = 9687, upload-time = "2026-03-20T14:38:13.272Z" } +sdist = { url = "https://files.pythonhosted.org/packages/51/a0/2407d616fdf68e8632bbbfb063d1685c38377ac0199e8ca11deaea1f3bf0/supabase-2.29.0.tar.gz", hash = "sha256:a88c4a4eb50fbb903e2e962fbc7c27733b00589140139f9e837bc9fe30dd3615", size = 9689, upload-time = "2026-04-24T13:13:06.728Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/de/96/1b48eb664153401c22087bbf77f6a428965e830cc8e0d0c6d68324a28342/supabase-2.28.3-py3-none-any.whl", hash = "sha256:52a7ce4a1d2d55fa6d657bf4760672935058143a5bedc64165851be25ce01dbd", size = 16634, upload-time = "2026-03-20T14:38:12.319Z" }, + { url = "https://files.pythonhosted.org/packages/22/52/232f6bbf5326e04ae12e2ef04a24f011a0d7cab379a8b9698652bc8ff78f/supabase-2.29.0-py3-none-any.whl", hash = "sha256:16c3ec4b7094f6b92efc5cd3bb3f96826d3b6dd5d24fe15c89c81166efce88fe", size = 16633, upload-time = "2026-04-24T13:13:05.722Z" }, ] [[package]] name = "supabase-auth" -version = "2.28.3" +version = "2.29.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx", extra = ["http2"] }, { name = "pydantic" }, { name = "pyjwt", extra = ["crypto"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cc/6f/1bf81293374ba71183b321bf5dfd7151c3db0c2e24715f35783bc1c56385/supabase_auth-2.28.3.tar.gz", hash = "sha256:41c049da82f9d7fc2f111808e57e984015f128d033f58caa67fd76f428472807", size = 39160, upload-time = "2026-03-20T14:38:15.128Z" } +sdist = { url = "https://files.pythonhosted.org/packages/51/7f/7ceeb4c7a2caa188062e934897f0e08e1af0a0e47e376c7645c26b4c39d8/supabase_auth-2.29.0.tar.gz", hash = "sha256:46efc6a3455a23957b846dc974303a844ba0413718cfa899425477ac977f95b3", size = 39154, upload-time = "2026-04-24T13:13:08.509Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c3/d3/e012315aa895b434fa77bc475e2dfeb87119e67918ecca4d88a25f96814d/supabase_auth-2.28.3-py3-none-any.whl", hash = "sha256:e47c5caec7bbf3c258964d027fbbe99f3cc4a956d3a635f898c962b4d22832dd", size = 48378, upload-time = "2026-03-20T14:38:14.169Z" }, + { url = "https://files.pythonhosted.org/packages/f1/ac/3c35cf52281f940b9497cf17abfc5c2050ca49f342d60cfafe22dac3482b/supabase_auth-2.29.0-py3-none-any.whl", hash = "sha256:64de6ef8cae80f97d3aa8d5ca507d5427dda5c89885c0bcfe9f8b0263b6fb9a4", size = 48379, upload-time = "2026-04-24T13:13:07.417Z" }, ] [[package]] name = "supabase-functions" -version = "2.28.3" +version = "2.29.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx", extra = ["http2"] }, { name = "strenum" }, { name = "yarl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/19/ea/59bf327960e5384fcc9e69afbdf97260a2cf2684a25c0731968a8a393b9c/supabase_functions-2.28.3.tar.gz", hash = "sha256:5a6255d60a263d44251c5ca250fcdde2408a8483a8bf31f4ac80255de8f3fcae", size = 4679, upload-time = "2026-03-20T14:38:16.742Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/19/1a1d22749f38f2a6cbca93a6f5a35c9f816c2c3c06bfaa077fa336e90537/supabase_functions-2.29.0.tar.gz", hash = "sha256:0f8a14a2ea9f12b1c208f61dc6f55e2f4b1121f81bf01c08f9b487d22888744d", size = 4683, upload-time = "2026-04-24T13:13:10.432Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/ca/1e720f1347a88519e3d52b6d801cd031c3a7a5df66640c5dc6e81d925057/supabase_functions-2.28.3-py3-none-any.whl", hash = "sha256:eb30578866103fed9322c54e95dd68c2f1a4b6b177e129d9369edd364637904e", size = 8801, upload-time = "2026-03-20T14:38:15.883Z" }, + { url = "https://files.pythonhosted.org/packages/e0/10/6f8ef0b408ade76b5a439afab588ce5849e9604a23040ca73cfe0b90cb9e/supabase_functions-2.29.0-py3-none-any.whl", hash = "sha256:6f08de52eec5820eae53616868b85e849e181beffaa5d05b8ea1708ceae5e48e", size = 8799, upload-time = "2026-04-24T13:13:09.214Z" }, ] [[package]] diff --git a/eslint-suppressions.json b/eslint-suppressions.json index 1bff82ac17..1e7a2662ed 100644 --- a/eslint-suppressions.json +++ b/eslint-suppressions.json @@ -2422,21 +2422,11 @@ "count": 1 } }, - "web/app/components/datasets/documents/create-from-pipeline/data-source/base/header.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/index.tsx": { "ts/no-explicit-any": { "count": 1 } }, - "web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/bucket.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/list/item.tsx": { "no-restricted-imports": { "count": 1 @@ -2525,11 +2515,6 @@ "count": 1 } }, - "web/app/components/datasets/documents/detail/completed/common/summary-status.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "web/app/components/datasets/documents/detail/completed/components/index.ts": { "no-barrel-files/no-barrel-files": { "count": 3 @@ -2789,11 +2774,6 @@ "count": 2 } }, - "web/app/components/develop/secret-key/input-copy.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "web/app/components/develop/secret-key/secret-key-generate.tsx": { "no-restricted-imports": { "count": 1 @@ -3159,16 +3139,6 @@ "count": 1 } }, - "web/app/components/plugins/base/badges/icon-with-tooltip.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "web/app/components/plugins/base/key-value-item.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "web/app/components/plugins/card/index.tsx": { "ts/no-non-null-asserted-optional-chain": { "count": 1 @@ -3328,24 +3298,11 @@ "count": 2 } }, - "web/app/components/plugins/plugin-detail-panel/detail-header/components/plugin-source-badge.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "web/app/components/plugins/plugin-detail-panel/detail-header/hooks/index.ts": { "no-barrel-files/no-barrel-files": { "count": 3 } }, - "web/app/components/plugins/plugin-detail-panel/endpoint-card.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "ts/no-explicit-any": { - "count": 2 - } - }, "web/app/components/plugins/plugin-detail-panel/endpoint-list.tsx": { "no-restricted-imports": { "count": 1 @@ -3544,11 +3501,6 @@ "count": 1 } }, - "web/app/components/plugins/plugin-page/plugin-tasks/components/task-status-indicator.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "web/app/components/plugins/readme-panel/index.tsx": { "react/unsupported-syntax": { "count": 1 @@ -3822,14 +3774,6 @@ "count": 1 } }, - "web/app/components/tools/mcp/detail/content.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "ts/no-explicit-any": { - "count": 3 - } - }, "web/app/components/tools/mcp/detail/tool-item.tsx": { "no-restricted-imports": { "count": 1 @@ -5394,14 +5338,6 @@ "count": 2 } }, - "web/app/components/workflow/panel/chat-variable-panel/components/variable-type-select.tsx": { - "no-restricted-imports": { - "count": 1 - }, - "ts/no-explicit-any": { - "count": 4 - } - }, "web/app/components/workflow/panel/chat-variable-panel/type.ts": { "erasable-syntax-only/enums": { "count": 1 diff --git a/packages/dify-ui/src/select/__tests__/index.spec.tsx b/packages/dify-ui/src/select/__tests__/index.spec.tsx index eab980a607..9e3e945de0 100644 --- a/packages/dify-ui/src/select/__tests__/index.spec.tsx +++ b/packages/dify-ui/src/select/__tests__/index.spec.tsx @@ -231,10 +231,8 @@ describe('Select wrappers', () => { , ) - screen.getByRole('group', { name: 'select positioner' }).element().dispatchEvent(new MouseEvent('mouseover', { - bubbles: true, - })) - asHTMLElement(screen.getByRole('dialog', { name: 'select popup' }).element()).click() + await screen.getByRole('group', { name: 'select positioner' }).hover() + await screen.getByRole('dialog', { name: 'select popup' }).click() screen.getByRole('listbox', { name: 'select list' }).element().dispatchEvent(new FocusEvent('focusin', { bubbles: true, })) diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/base/__tests__/header.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/base/__tests__/header.spec.tsx index a6abad358e..bc3b025ded 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/base/__tests__/header.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/base/__tests__/header.spec.tsx @@ -2,18 +2,10 @@ import { render, screen } from '@testing-library/react' import { describe, expect, it, vi } from 'vitest' import Header from '../header' -vi.mock('@langgenius/dify-ui/button', () => ({ - Button: ({ children }: { children: React.ReactNode }) => , -})) - vi.mock('@/app/components/base/divider', () => ({ default: () => , })) -vi.mock('@/app/components/base/tooltip', () => ({ - default: ({ children }: { children: React.ReactNode }) =>
{children}
, -})) - vi.mock('../credential-selector', () => ({ default: () =>
, })) diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/base/header.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/base/header.tsx index a285946272..c91012bf4a 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/base/header.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/base/header.tsx @@ -1,10 +1,9 @@ import type { CredentialSelectorProps } from './credential-selector' import { Button } from '@langgenius/dify-ui/button' -import { RiBookOpenLine, RiEqualizer2Line } from '@remixicon/react' +import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip' import * as React from 'react' import { useTranslation } from 'react-i18next' import Divider from '@/app/components/base/divider' -import Tooltip from '@/app/components/base/tooltip' import CredentialSelector from './credential-selector' type HeaderProps = { @@ -22,6 +21,7 @@ const Header = ({ ...rest }: HeaderProps) => { const { t } = useTranslation() + const configurationTip = t('configurationTip', { ns: 'datasetPipeline', pluginName }) return (
@@ -30,20 +30,23 @@ const Header = ({ {...rest} /> - - + + + + + )} + /> + + {configurationTip} +
- + {docTitle}
diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/__tests__/bucket.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/__tests__/bucket.spec.tsx index 83e17e6e04..b0a49eee0d 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/__tests__/bucket.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/__tests__/bucket.spec.tsx @@ -5,9 +5,6 @@ import Bucket from '../bucket' vi.mock('@/app/components/base/icons/src/public/knowledge/online-drive', () => ({ BucketsGray: (props: React.SVGProps) => , })) -vi.mock('@/app/components/base/tooltip', () => ({ - default: ({ children }: { children?: React.ReactNode }) =>
{children}
, -})) describe('Bucket', () => { const defaultProps = { @@ -32,8 +29,7 @@ describe('Bucket', () => { it('should call handleBackToBucketList on icon button click', () => { render() - const buttons = screen.getAllByRole('button') - fireEvent.click(buttons[0]!) + fireEvent.click(screen.getByRole('button', { name: 'datasetPipeline.onlineDrive.breadcrumbs.allBuckets' })) expect(defaultProps.handleBackToBucketList).toHaveBeenCalledOnce() }) diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/bucket.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/bucket.tsx index 003aee6542..384188502b 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/bucket.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/bucket.tsx @@ -1,9 +1,10 @@ +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' +import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip' import * as React from 'react' import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { BucketsGray } from '@/app/components/base/icons/src/public/knowledge/online-drive' -import Tooltip from '@/app/components/base/tooltip' type BucketProps = { bucketName: string @@ -27,19 +28,28 @@ const Bucket = ({ if (!disabled) handleClickBucketName() }, [disabled, handleClickBucketName]) + const allBucketsLabel = t('onlineDrive.breadcrumbs.allBuckets', { ns: 'datasetPipeline' }) return ( <> - - + + + + + )} + /> + + {allBucketsLabel} + / + default: ({ + children, + onClick, + ...props + }: React.ButtonHTMLAttributes) => ( + ), })) @@ -54,6 +52,6 @@ describe('KeyValueItem', () => { it('renders copy tooltip', () => { render() - expect(screen.getByTestId('tooltip')).toHaveAttribute('data-content', 'common.operation.copy') + expect(screen.getByRole('button', { name: 'common.operation.copy' })).toBeInTheDocument() }) }) diff --git a/web/app/components/plugins/base/badges/__tests__/icon-with-tooltip.spec.tsx b/web/app/components/plugins/base/badges/__tests__/icon-with-tooltip.spec.tsx index e24aa5a873..d4a87fa8a5 100644 --- a/web/app/components/plugins/base/badges/__tests__/icon-with-tooltip.spec.tsx +++ b/web/app/components/plugins/base/badges/__tests__/icon-with-tooltip.spec.tsx @@ -3,24 +3,6 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' import { Theme } from '@/types/app' import IconWithTooltip from '../icon-with-tooltip' -// Mock Tooltip component -vi.mock('@/app/components/base/tooltip', () => ({ - default: ({ - children, - popupContent, - popupClassName, - }: { - children: React.ReactNode - popupContent?: string - popupClassName?: string - }) => ( -
- {children} -
- ), -})) - -// Mock icon components const MockLightIcon = ({ className }: { className?: string }) => (
Light Icon
) @@ -44,10 +26,10 @@ describe('IconWithTooltip', () => { />, ) - expect(screen.getByTestId('tooltip')).toBeInTheDocument() + expect(screen.getByTestId('light-icon')).toBeInTheDocument() }) - it('should render Tooltip wrapper', () => { + it('should render tooltip trigger with accessible label when popupContent is provided', () => { render( { />, ) - expect(screen.getByTestId('tooltip')).toHaveAttribute('data-popup-content', 'Test tooltip') - }) - - it('should apply correct popupClassName to Tooltip', () => { - render( - , - ) - - const tooltip = screen.getByTestId('tooltip') - expect(tooltip).toHaveAttribute('data-popup-classname') - expect(tooltip.getAttribute('data-popup-classname')).toContain('border-components-panel-border') + expect(screen.getByLabelText('Test tooltip')).toBeInTheDocument() }) }) @@ -171,10 +139,7 @@ describe('IconWithTooltip', () => { />, ) - expect(screen.getByTestId('tooltip')).toHaveAttribute( - 'data-popup-content', - 'Custom tooltip content', - ) + expect(screen.getByLabelText('Custom tooltip content')).toBeInTheDocument() }) it('should handle undefined popupContent', () => { @@ -186,7 +151,7 @@ describe('IconWithTooltip', () => { />, ) - expect(screen.getByTestId('tooltip')).toBeInTheDocument() + expect(screen.getByTestId('light-icon')).toBeInTheDocument() }) }) @@ -239,7 +204,7 @@ describe('IconWithTooltip', () => { />, ) - expect(screen.getByTestId('tooltip')).toHaveAttribute('data-popup-content', longContent) + expect(screen.getByLabelText(longContent)).toBeInTheDocument() }) it('should handle special characters in popupContent', () => { @@ -253,7 +218,7 @@ describe('IconWithTooltip', () => { />, ) - expect(screen.getByTestId('tooltip')).toHaveAttribute('data-popup-content', specialContent) + expect(screen.getByLabelText(specialContent)).toBeInTheDocument() }) }) }) diff --git a/web/app/components/plugins/base/badges/icon-with-tooltip.tsx b/web/app/components/plugins/base/badges/icon-with-tooltip.tsx index faabd545fd..2cb40adf0a 100644 --- a/web/app/components/plugins/base/badges/icon-with-tooltip.tsx +++ b/web/app/components/plugins/base/badges/icon-with-tooltip.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import { cn } from '@langgenius/dify-ui/cn' +import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip' import * as React from 'react' -import Tooltip from '@/app/components/base/tooltip' import { Theme } from '@/types/app' type IconWithTooltipProps = { @@ -22,15 +22,24 @@ const IconWithTooltip: FC = ({ const isDark = theme === Theme.dark const iconClassName = cn('h-5 w-5', className) const Icon = isDark ? BadgeIconDark : BadgeIconLight + const icon = ( + + + + ) + + if (!popupContent) + return icon return ( - -
- -
+ + + + {popupContent} + ) } diff --git a/web/app/components/plugins/base/key-value-item.tsx b/web/app/components/plugins/base/key-value-item.tsx index 1ba8e8caf9..a2a3459b5d 100644 --- a/web/app/components/plugins/base/key-value-item.tsx +++ b/web/app/components/plugins/base/key-value-item.tsx @@ -1,16 +1,13 @@ 'use client' import type { FC } from 'react' import { cn } from '@langgenius/dify-ui/cn' -import { - RiClipboardLine, -} from '@remixicon/react' +import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip' import copy from 'copy-to-clipboard' import * as React from 'react' import { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import { CopyCheck } from '../../base/icons/src/vender/line/files' -import Tooltip from '../../base/tooltip' type Props = { label: string @@ -45,7 +42,7 @@ const KeyValueItem: FC = ({ } }, [isCopied]) - const CopyIcon = isCopied ? CopyCheck : RiClipboardLine + const copyLabel = t(`operation.${isCopied ? 'copied' : 'copy'}`, { ns: 'common' }) return (
@@ -54,10 +51,19 @@ const KeyValueItem: FC = ({ {maskedValue || value} - - - - + + + {isCopied + ? + : } + + )} + /> + + {copyLabel} +
diff --git a/web/app/components/plugins/plugin-detail-panel/detail-header/components/__tests__/plugin-source-badge.spec.tsx b/web/app/components/plugins/plugin-detail-panel/detail-header/components/__tests__/plugin-source-badge.spec.tsx index 4d60433efb..08f5f836f4 100644 --- a/web/app/components/plugins/plugin-detail-panel/detail-header/components/__tests__/plugin-source-badge.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/detail-header/components/__tests__/plugin-source-badge.spec.tsx @@ -3,14 +3,6 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' import { PluginSource } from '../../../../types' import PluginSourceBadge from '../plugin-source-badge' -vi.mock('@/app/components/base/tooltip', () => ({ - default: ({ children, popupContent }: { children: React.ReactNode, popupContent: string }) => ( -
- {children} -
- ), -})) - describe('PluginSourceBadge', () => { beforeEach(() => { vi.clearAllMocks() @@ -20,33 +12,25 @@ describe('PluginSourceBadge', () => { it('should render marketplace source badge', () => { render() - const tooltip = screen.getByTestId('tooltip') - expect(tooltip).toBeInTheDocument() - expect(tooltip).toHaveAttribute('data-content', 'plugin.detailPanel.categoryTip.marketplace') + expect(screen.getByLabelText('plugin.detailPanel.categoryTip.marketplace')).toBeInTheDocument() }) it('should render github source badge', () => { render() - const tooltip = screen.getByTestId('tooltip') - expect(tooltip).toBeInTheDocument() - expect(tooltip).toHaveAttribute('data-content', 'plugin.detailPanel.categoryTip.github') + expect(screen.getByLabelText('plugin.detailPanel.categoryTip.github')).toBeInTheDocument() }) it('should render local source badge', () => { render() - const tooltip = screen.getByTestId('tooltip') - expect(tooltip).toBeInTheDocument() - expect(tooltip).toHaveAttribute('data-content', 'plugin.detailPanel.categoryTip.local') + expect(screen.getByLabelText('plugin.detailPanel.categoryTip.local')).toBeInTheDocument() }) it('should render debugging source badge', () => { render() - const tooltip = screen.getByTestId('tooltip') - expect(tooltip).toBeInTheDocument() - expect(tooltip).toHaveAttribute('data-content', 'plugin.detailPanel.categoryTip.debugging') + expect(screen.getByLabelText('plugin.detailPanel.categoryTip.debugging')).toBeInTheDocument() }) }) @@ -86,71 +70,47 @@ describe('PluginSourceBadge', () => { it('should show marketplace tooltip', () => { render() - expect(screen.getByTestId('tooltip')).toHaveAttribute( - 'data-content', - 'plugin.detailPanel.categoryTip.marketplace', - ) + expect(screen.getByLabelText('plugin.detailPanel.categoryTip.marketplace')).toBeInTheDocument() }) it('should show github tooltip', () => { render() - expect(screen.getByTestId('tooltip')).toHaveAttribute( - 'data-content', - 'plugin.detailPanel.categoryTip.github', - ) + expect(screen.getByLabelText('plugin.detailPanel.categoryTip.github')).toBeInTheDocument() }) it('should show local tooltip', () => { render() - expect(screen.getByTestId('tooltip')).toHaveAttribute( - 'data-content', - 'plugin.detailPanel.categoryTip.local', - ) + expect(screen.getByLabelText('plugin.detailPanel.categoryTip.local')).toBeInTheDocument() }) it('should show debugging tooltip', () => { render() - expect(screen.getByTestId('tooltip')).toHaveAttribute( - 'data-content', - 'plugin.detailPanel.categoryTip.debugging', - ) + expect(screen.getByLabelText('plugin.detailPanel.categoryTip.debugging')).toBeInTheDocument() }) }) describe('Icon Element Structure', () => { it('should render icon inside tooltip for marketplace', () => { - render() - - const tooltip = screen.getByTestId('tooltip') - const iconWrapper = tooltip.querySelector('div') - expect(iconWrapper).toBeInTheDocument() + const { container } = render() + expect(container.querySelector('[aria-label="plugin.detailPanel.categoryTip.marketplace"]')).toBeInTheDocument() }) it('should render icon inside tooltip for github', () => { - render() - - const tooltip = screen.getByTestId('tooltip') - const iconWrapper = tooltip.querySelector('div') - expect(iconWrapper).toBeInTheDocument() + const { container } = render() + expect(container.querySelector('[aria-label="plugin.detailPanel.categoryTip.github"]')).toBeInTheDocument() }) it('should render icon inside tooltip for local', () => { - render() - - const tooltip = screen.getByTestId('tooltip') - const iconWrapper = tooltip.querySelector('div') - expect(iconWrapper).toBeInTheDocument() + const { container } = render() + expect(container.querySelector('[aria-label="plugin.detailPanel.categoryTip.local"]')).toBeInTheDocument() }) it('should render icon inside tooltip for debugging', () => { - render() - - const tooltip = screen.getByTestId('tooltip') - const iconWrapper = tooltip.querySelector('div') - expect(iconWrapper).toBeInTheDocument() + const { container } = render() + expect(container.querySelector('[aria-label="plugin.detailPanel.categoryTip.debugging"]')).toBeInTheDocument() }) }) @@ -188,7 +148,7 @@ describe('PluginSourceBadge', () => { const invalidSource = '' as PluginSource render() - expect(screen.queryByTestId('tooltip')).not.toBeInTheDocument() + expect(screen.queryByLabelText(/^plugin\.detailPanel\.categoryTip\./)).not.toBeInTheDocument() }) }) }) diff --git a/web/app/components/plugins/plugin-detail-panel/detail-header/components/plugin-source-badge.tsx b/web/app/components/plugins/plugin-detail-panel/detail-header/components/plugin-source-badge.tsx index ba15815cde..9b6725da14 100644 --- a/web/app/components/plugins/plugin-detail-panel/detail-header/components/plugin-source-badge.tsx +++ b/web/app/components/plugins/plugin-detail-panel/detail-header/components/plugin-source-badge.tsx @@ -1,14 +1,10 @@ 'use client' import type { FC, ReactNode } from 'react' -import { - RiBugLine, - RiHardDrive3Line, -} from '@remixicon/react' +import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip' import { useTranslation } from 'react-i18next' import { Github } from '@/app/components/base/icons/src/public/common' import { BoxSparkleFill } from '@/app/components/base/icons/src/vender/plugin' -import Tooltip from '@/app/components/base/tooltip' import { PluginSource } from '../../../types' type SourceConfig = { @@ -30,11 +26,11 @@ const SOURCE_CONFIG_MAP: Record = { tipKey: 'detailPanel.categoryTip.github', }, [PluginSource.local]: { - icon: , + icon: , tipKey: 'detailPanel.categoryTip.local', }, [PluginSource.debugging]: { - icon: , + icon: , tipKey: 'detailPanel.categoryTip.debugging', }, } @@ -45,12 +41,22 @@ const PluginSourceBadge: FC = ({ source }) => { const config = SOURCE_CONFIG_MAP[source] if (!config) return null + const tip = t(config.tipKey as never, { ns: 'plugin' }) return ( <>
·
- -
{config.icon}
+ + + {config.icon} +
+ )} + /> + + {tip} +
) diff --git a/web/app/components/plugins/plugin-detail-panel/endpoint-card.tsx b/web/app/components/plugins/plugin-detail-panel/endpoint-card.tsx index e1adc6282d..9aa944c4b3 100644 --- a/web/app/components/plugins/plugin-detail-panel/endpoint-card.tsx +++ b/web/app/components/plugins/plugin-detail-panel/endpoint-card.tsx @@ -1,3 +1,4 @@ +import type { ComponentProps } from 'react' import type { EndpointListItem, PluginDetail } from '../types' import { AlertDialog, @@ -9,7 +10,7 @@ import { } from '@langgenius/dify-ui/alert-dialog' import { Switch } from '@langgenius/dify-ui/switch' import { toast } from '@langgenius/dify-ui/toast' -import { RiClipboardLine, RiDeleteBinLine, RiEditLine, RiLoginCircleLine } from '@remixicon/react' +import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip' import { useBoolean } from 'ahooks' import copy from 'copy-to-clipboard' import * as React from 'react' @@ -17,7 +18,6 @@ import { useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import { CopyCheck } from '@/app/components/base/icons/src/vender/line/files' -import Tooltip from '@/app/components/base/tooltip' import Indicator from '@/app/components/header/indicator' import { addDefaultValue, toolCredentialToFormSchemas } from '@/app/components/tools/utils/to-form-schema' import { @@ -29,6 +29,8 @@ import { import EndpointModal from './endpoint-modal' import { NAME_FIELD } from './utils' +type EndpointModalFormSchemas = ComponentProps['formSchemas'] + type Props = { pluginDetail: PluginDetail data: EndpointListItem @@ -118,7 +120,7 @@ const EndpointCard = ({ toast.error(t('actionMsg.modifiedUnsuccessfully', { ns: 'common' })) }, }) - const handleUpdate = (state: Record) => updateEndpoint({ + const handleUpdate = (state: Record) => updateEndpoint({ endpointID, state, }) @@ -148,22 +150,22 @@ const EndpointCard = ({ } }, [isCopied]) - const CopyIcon = isCopied ? CopyCheck : RiClipboardLine + const copyLabel = t(`operation.${isCopied ? 'copied' : 'copy'}`, { ns: 'common' }) return (
- +
{data.name}
- + - +
@@ -172,10 +174,23 @@ const EndpointCard = ({
{endpoint.method}
{`${data.url}${endpoint.path}`}
- - handleCopy(`${data.url}${endpoint.path}`)}> - - + + handleCopy(`${data.url}${endpoint.path}`)} + > + {isCopied + ? + : } + + )} + /> + + {copyLabel} +
@@ -244,7 +259,7 @@ const EndpointCard = ({ {isShowEndpointModal && ( ({ ), })) -vi.mock('@/app/components/base/tooltip', () => ({ - default: ({ children, popupContent }: { children: React.ReactNode, popupContent: string }) => ( -
{children}
- ), -})) - vi.mock('@/app/components/header/plugins-nav/downloading-icon', () => ({ default: () => , })) @@ -38,18 +32,17 @@ describe('TaskStatusIndicator', () => { describe('Rendering', () => { it('should render without crashing', () => { render() - expect(screen.getByTestId('tooltip')).toBeInTheDocument() + expect(screen.getByRole('button', { name: 'Installing plugins' })).toBeInTheDocument() }) - it('should pass tip to tooltip', () => { + it('should use tip as the trigger accessible name', () => { render() - expect(screen.getByTestId('tooltip')).toHaveAttribute('data-tip', 'My tip') + expect(screen.getByRole('button', { name: 'My tip' })).toBeInTheDocument() }) it('should render install icon by default', () => { const { container } = render() - // RiInstallLine renders as svg - expect(container.querySelector('svg')).toBeInTheDocument() + expect(container.querySelector('.i-ri-install-line')).toBeInTheDocument() expect(screen.queryByTestId('downloading-icon')).not.toBeInTheDocument() }) }) @@ -127,7 +120,6 @@ describe('TaskStatusIndicator', () => { totalPluginsLength={3} />, ) - // RiCheckboxCircleFill is rendered as svg with text-text-success const successIcon = container.querySelector('.text-text-success') expect(successIcon).toBeInTheDocument() }) diff --git a/web/app/components/plugins/plugin-page/plugin-tasks/components/task-status-indicator.tsx b/web/app/components/plugins/plugin-page/plugin-tasks/components/task-status-indicator.tsx index d1de645f7b..691ee40f4d 100644 --- a/web/app/components/plugins/plugin-page/plugin-tasks/components/task-status-indicator.tsx +++ b/web/app/components/plugins/plugin-page/plugin-tasks/components/task-status-indicator.tsx @@ -1,12 +1,8 @@ import type { FC } from 'react' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' -import { - RiCheckboxCircleFill, - RiErrorWarningFill, - RiInstallLine, -} from '@remixicon/react' +import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip' import ProgressCircle from '@/app/components/base/progress-bar/progress-circle' -import Tooltip from '@/app/components/base/tooltip' import DownloadingIcon from '@/app/components/header/plugins-nav/downloading-icon' type TaskStatusIndicatorProps = { @@ -39,56 +35,61 @@ const TaskStatusIndicator: FC = ({ const showSuccessIcon = isSuccess || (successPluginsLength > 0 && runningPluginsLength === 0) return ( - -
- {/* Main Icon */} - {showDownloadingIcon - ? - : ( - + + + {showDownloadingIcon + ? + : ( + + )} - {/* Status Indicator Badge */} -
- {(isInstalling || isInstallingWithSuccess) && ( - 0 ? successPluginsLength / totalPluginsLength : 0) * 100} - circleFillColor="fill-components-progress-brand-bg" - /> - )} - {isInstallingWithError && ( - 0 ? runningPluginsLength / totalPluginsLength : 0) * 100} - circleFillColor="fill-components-progress-brand-bg" - sectorFillColor="fill-components-progress-error-border" - circleStrokeColor="stroke-components-progress-error-border" - /> - )} - {showSuccessIcon && !isInstalling && !isInstallingWithSuccess && !isInstallingWithError && ( - - )} - {isFailed && ( - - )} -
-
+
+ {(isInstalling || isInstallingWithSuccess) && ( + 0 ? successPluginsLength / totalPluginsLength : 0) * 100} + circleFillColor="fill-components-progress-brand-bg" + /> + )} + {isInstallingWithError && ( + 0 ? runningPluginsLength / totalPluginsLength : 0) * 100} + circleFillColor="fill-components-progress-brand-bg" + sectorFillColor="fill-components-progress-error-border" + circleStrokeColor="stroke-components-progress-error-border" + /> + )} + {showSuccessIcon && !isInstalling && !isInstallingWithSuccess && !isInstallingWithError && ( + + )} + {isFailed && ( + + )} +
+ + )} + /> + {tip}
) } diff --git a/web/app/components/tools/mcp/detail/__tests__/content.spec.tsx b/web/app/components/tools/mcp/detail/__tests__/content.spec.tsx index 5216e9eede..f7bf8181ed 100644 --- a/web/app/components/tools/mcp/detail/__tests__/content.spec.tsx +++ b/web/app/components/tools/mcp/detail/__tests__/content.spec.tsx @@ -698,16 +698,9 @@ describe('MCPDetailContent', () => { const onHide = vi.fn() render(, { wrapper: createWrapper() }) - // Find the close button (ActionButton with RiCloseLine) - const buttons = screen.getAllByRole('button') - const closeButton = buttons.find(btn => - btn.querySelector('svg.h-4.w-4'), - ) + fireEvent.click(screen.getByRole('button', { name: 'common.operation.close' })) - if (closeButton) { - fireEvent.click(closeButton) - expect(onHide).toHaveBeenCalled() - } + expect(onHide).toHaveBeenCalled() }) }) diff --git a/web/app/components/tools/mcp/detail/content.tsx b/web/app/components/tools/mcp/detail/content.tsx index 48ea75723c..35c8a35a6f 100644 --- a/web/app/components/tools/mcp/detail/content.tsx +++ b/web/app/components/tools/mcp/detail/content.tsx @@ -1,5 +1,5 @@ 'use client' -import type { FC } from 'react' +import type { ComponentProps, FC } from 'react' import type { ToolWithProvider } from '../../../workflow/types' import { AlertDialog, @@ -12,18 +12,13 @@ import { } from '@langgenius/dify-ui/alert-dialog' import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' -import { - RiCloseLine, - RiLoader2Line, - RiLoopLeftLine, -} from '@remixicon/react' +import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip' import { useBoolean } from 'ahooks' import copy from 'copy-to-clipboard' import * as React from 'react' import { useCallback, useEffect } from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' -import Tooltip from '@/app/components/base/tooltip' import Indicator from '@/app/components/header/indicator' import Icon from '@/app/components/plugins/card/base/card-icon' import { useAppContext } from '@/context/app-context' @@ -49,6 +44,11 @@ type Props = { onFirstCreate: () => void } +type MCPModalConfirmPayload = Parameters['onConfirm']>[0] +type MutationResult = { + result?: string +} + const MCPDetailContent: FC = ({ detail, onUpdate, @@ -128,14 +128,14 @@ const MCPDetailContent: FC = ({ } }, [onFirstCreate, isCurrentWorkspaceManager, detail, authorizeMcp, handleUpdateTools, handleOAuthCallback, onUpdate]) - const handleUpdate = useCallback(async (data: any) => { + const handleUpdate = useCallback(async (data: MCPModalConfirmPayload) => { if (!detail) return const res = await updateMCP({ ...data, provider_id: detail.id, - }) - if ((res as any)?.result === 'success') { + }) as MutationResult + if (res.result === 'success') { hideUpdateModal() onUpdate() handleAuthorize() @@ -146,9 +146,9 @@ const MCPDetailContent: FC = ({ if (!detail) return showDeleting() - const res = await deleteMCP(detail.id) + const res = await deleteMCP(detail.id) as MutationResult hideDeleting() - if ((res as any)?.result === 'success') { + if (res.result === 'success') { hideDeleteConfirm() onUpdate(true) } @@ -161,6 +161,8 @@ const MCPDetailContent: FC = ({ if (!detail) return null + const identifierLabel = t('mcp.identifier', { ns: 'tools' }) + const serverUrlLabel = t('mcp.modal.serverUrl', { ns: 'tools' }) return ( <> @@ -174,12 +176,37 @@ const MCPDetailContent: FC = ({
{detail.name}
- -
copy(detail.server_identifier || '')}>{detail.server_identifier}
+ + copy(detail.server_identifier || '')} + > + {detail.server_identifier} + + )} + /> + + {identifierLabel} +
·
- -
{detail.server_url}
+ + + {detail.server_url} +
+ )} + /> + + {serverUrlLabel} +
@@ -188,8 +215,8 @@ const MCPDetailContent: FC = ({ onEdit={showUpdateModal} onRemove={showDeleteConfirm} /> - - + + @@ -221,7 +248,7 @@ const MCPDetailContent: FC = ({ className="w-full" disabled > - + {t('mcp.authorizing', { ns: 'tools' })} )} @@ -262,7 +289,7 @@ const MCPDetailContent: FC = ({
diff --git a/web/app/components/workflow/panel/chat-variable-panel/components/__tests__/variable-type-select.spec.tsx b/web/app/components/workflow/panel/chat-variable-panel/components/__tests__/variable-type-select.spec.tsx index 3a7df8a3bf..d0831c319c 100644 --- a/web/app/components/workflow/panel/chat-variable-panel/components/__tests__/variable-type-select.spec.tsx +++ b/web/app/components/workflow/panel/chat-variable-panel/components/__tests__/variable-type-select.spec.tsx @@ -36,8 +36,9 @@ describe('VariableTypeSelector', () => { await user.keyboard('{Escape}') await waitFor(() => { - expect(screen.queryByText('number')).not.toBeInTheDocument() + expect(screen.getByRole('combobox')).toHaveAttribute('aria-expanded', 'false') }) + expect(screen.queryByRole('listbox')).not.toBeInTheDocument() }) it('keeps the custom popup class in in-cell mode', async () => { diff --git a/web/app/components/workflow/panel/chat-variable-panel/components/variable-type-select.tsx b/web/app/components/workflow/panel/chat-variable-panel/components/variable-type-select.tsx index 94a0100de2..e1f776f3d5 100644 --- a/web/app/components/workflow/panel/chat-variable-panel/components/variable-type-select.tsx +++ b/web/app/components/workflow/panel/chat-variable-panel/components/variable-type-select.tsx @@ -1,38 +1,47 @@ 'use client' import { cn } from '@langgenius/dify-ui/cn' -import { RiArrowDownSLine, RiCheckLine } from '@remixicon/react' +import { Select, SelectContent, SelectItem, SelectItemIndicator, SelectItemText, SelectTrigger } from '@langgenius/dify-ui/select' import * as React from 'react' import { useState } from 'react' -import { - PortalToFollowElem, - PortalToFollowElemContent, - PortalToFollowElemTrigger, -} from '@/app/components/base/portal-to-follow-elem' -type Props = { +type Props = { inCell?: boolean - value?: any - list: any - onSelect: (value: any) => void + value?: T + list: readonly T[] + onSelect: (value: T) => void popupClassName?: string } -const VariableTypeSelector = ({ +const VariableTypeSelector = ({ inCell = false, value, list, onSelect, popupClassName, -}: Props) => { +}: Props) => { const [open, setOpen] = useState(false) + const handleValueChange = (nextValue: string | null) => { + if (!nextValue) + return + + const nextItem = list.find(item => item === nextValue) + if (!nextItem) + return + + onSelect(nextItem) + } + return ( - setOpen(v => !v)} - placement="bottom" + onOpenChange={setOpen} + onValueChange={handleValueChange} > - setOpen(v => !v)}> +
{value}
- +
- -
- {list.map((item: any) => ( -
{ - onSelect(item) - setOpen(false) - }} - > -
{item}
- {value === item && } -
- ))} -
-
-
+ + + {list.map(item => ( + + {item} + + + ))} + + ) }