mirror of
https://github.com/apache/superset.git
synced 2026-06-13 19:49:18 +00:00
Compare commits
291 Commits
docs_opena
...
spinner-po
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bbd19f9772 | ||
|
|
45a032a2f8 | ||
|
|
0a07520dea | ||
|
|
1be1ca1eee | ||
|
|
253a72f58a | ||
|
|
ae98d5fa6a | ||
|
|
96d7209eb5 | ||
|
|
e6af4ea126 | ||
|
|
fa8819770e | ||
|
|
72fc7ac27f | ||
|
|
a64b9ac84f | ||
|
|
56089aab3a | ||
|
|
bce3d4f19e | ||
|
|
59e3645c17 | ||
|
|
e05ccb3824 | ||
|
|
86e7139245 | ||
|
|
bb6bd85c1d | ||
|
|
ca74ae75a6 | ||
|
|
ae6c072661 | ||
|
|
5f2f12d347 | ||
|
|
fc7ba060c1 | ||
|
|
3a3984006c | ||
|
|
d11b6d557e | ||
|
|
2f007bf7a5 | ||
|
|
6513445000 | ||
|
|
3ef92e5610 | ||
|
|
57bb425fb0 | ||
|
|
2fba789e8d | ||
|
|
08655a7559 | ||
|
|
33b069a580 | ||
|
|
1eb65ec7ac | ||
|
|
0cacaba495 | ||
|
|
a9f744f8a2 | ||
|
|
b41961a2a6 | ||
|
|
04d0053a89 | ||
|
|
84287be168 | ||
|
|
4676f5c756 | ||
|
|
0315a3723f | ||
|
|
3256008a59 | ||
|
|
da8efd36d7 | ||
|
|
5541dad32b | ||
|
|
b3f436a030 | ||
|
|
b00660acf1 | ||
|
|
20a33b0744 | ||
|
|
3e34efef52 | ||
|
|
a6af4f4d7a | ||
|
|
cc3460832f | ||
|
|
edc60914f6 | ||
|
|
c9518485ba | ||
|
|
a26e1d822a | ||
|
|
a7aa8f7cef | ||
|
|
ff34e3c81e | ||
|
|
54e2c96720 | ||
|
|
fd7b8f6bd1 | ||
|
|
3eb04b0867 | ||
|
|
90967c6bb1 | ||
|
|
20519158d2 | ||
|
|
cacf1e06d6 | ||
|
|
fa0c5891bf | ||
|
|
fc13a0fde5 | ||
|
|
ade85daee2 | ||
|
|
2d26af25c1 | ||
|
|
b033406387 | ||
|
|
ef8ba1cbeb | ||
|
|
926d2bf031 | ||
|
|
bd77f82cc9 | ||
|
|
9c7b676bfc | ||
|
|
28db9ad7fc | ||
|
|
c09f8f6f76 | ||
|
|
401ce56fa1 | ||
|
|
cf315388f2 | ||
|
|
f219dc1794 | ||
|
|
ed20d2a917 | ||
|
|
235c9d2ebf | ||
|
|
fdea4e21b0 | ||
|
|
e20a08cb14 | ||
|
|
429935a277 | ||
|
|
58435e3e28 | ||
|
|
a4bb11c755 | ||
|
|
f0b6e87091 | ||
|
|
ea5a609d0b | ||
|
|
0abe6eed89 | ||
|
|
e205846845 | ||
|
|
deef923825 | ||
|
|
0fa3feb088 | ||
|
|
1393f7d3d2 | ||
|
|
b7ba50033a | ||
|
|
ce9759785a | ||
|
|
8de58b9848 | ||
|
|
cc8ab2c556 | ||
|
|
1409b1a25b | ||
|
|
8e48fdbd6f | ||
|
|
bdfb698aa4 | ||
|
|
57183da315 | ||
|
|
c928f23e1b | ||
|
|
0c89914a6d | ||
|
|
17ef5b67d1 | ||
|
|
c279d08d5e | ||
|
|
630e0e0240 | ||
|
|
513047c3bb | ||
|
|
d932837a3c | ||
|
|
38868f9ff4 | ||
|
|
8013b32f0e | ||
|
|
adeed60fe0 | ||
|
|
546945e7a6 | ||
|
|
5b2f1bbf9e | ||
|
|
875f538d54 | ||
|
|
2979c30703 | ||
|
|
b7d3ff1e85 | ||
|
|
c03964dc5f | ||
|
|
950a3313d8 | ||
|
|
20a17be0f3 | ||
|
|
e2a22d481c | ||
|
|
b4e2406385 | ||
|
|
ca9e74edd8 | ||
|
|
4070dba438 | ||
|
|
0af5770a49 | ||
|
|
39b3de6b5d | ||
|
|
26563bb330 | ||
|
|
0653e123cc | ||
|
|
76358ed64e | ||
|
|
217f11a8f7 | ||
|
|
af21ef2497 | ||
|
|
51c25831e8 | ||
|
|
be41e0526a | ||
|
|
0f240ea1b2 | ||
|
|
e520538af6 | ||
|
|
e03d840d06 | ||
|
|
1921ba993e | ||
|
|
b050897ebd | ||
|
|
0bdd8a223d | ||
|
|
d12f86363f | ||
|
|
9f680a63f8 | ||
|
|
33f2ffd2a9 | ||
|
|
928a052440 | ||
|
|
fbc84a1f9a | ||
|
|
fa1693dc5f | ||
|
|
8a8fb49617 | ||
|
|
dc4474889d | ||
|
|
fcea7e4af9 | ||
|
|
7258dc9ea0 | ||
|
|
29ac507d56 | ||
|
|
14645a2dfa | ||
|
|
6fe8a54b6f | ||
|
|
b9710f947c | ||
|
|
399788442a | ||
|
|
b03c425393 | ||
|
|
e862b5cddd | ||
|
|
5a67713f3f | ||
|
|
7f14e434c8 | ||
|
|
21ca26acd7 | ||
|
|
f00adac73e | ||
|
|
1c28138938 | ||
|
|
33e48146b0 | ||
|
|
cf68c879e2 | ||
|
|
5b991800c3 | ||
|
|
7472714ce7 | ||
|
|
67aa991099 | ||
|
|
73701b7295 | ||
|
|
22475e787e | ||
|
|
9e38a0cc29 | ||
|
|
a391ebecca | ||
|
|
5267ec2028 | ||
|
|
3983ee0c2f | ||
|
|
72cd9dffa3 | ||
|
|
863a0bea5c | ||
|
|
4ed05f4ff1 | ||
|
|
871cfe0c78 | ||
|
|
d635c2a9ab | ||
|
|
2c038f5bd6 | ||
|
|
a928f8cd9e | ||
|
|
afaaf64f52 | ||
|
|
fc031ca35b | ||
|
|
89424894f3 | ||
|
|
9da62ed63a | ||
|
|
49bcf79f71 | ||
|
|
e529d84e34 | ||
|
|
f580da88ca | ||
|
|
b040d52c2d | ||
|
|
a7b7e6319c | ||
|
|
c217f56aea | ||
|
|
d43657ed90 | ||
|
|
6c01173c21 | ||
|
|
4709eb0153 | ||
|
|
51f719f8d4 | ||
|
|
50535a92a0 | ||
|
|
3fe5db1e72 | ||
|
|
08c9d28545 | ||
|
|
86d5537a7f | ||
|
|
18c3f0adb6 | ||
|
|
5e8cd7a6ee | ||
|
|
dcea6c09ca | ||
|
|
dff7c1b50d | ||
|
|
4f93c2d2e7 | ||
|
|
334aa1a672 | ||
|
|
2667a14678 | ||
|
|
29ba5adf21 | ||
|
|
f18455cc2d | ||
|
|
4123d25873 | ||
|
|
662f33b7de | ||
|
|
c14dcecd8f | ||
|
|
6a4730bbbe | ||
|
|
739caa19cb | ||
|
|
8bb02e2958 | ||
|
|
7c2fd55104 | ||
|
|
6c8e72b889 | ||
|
|
3b198ab656 | ||
|
|
c993abe58c | ||
|
|
a9bc4655a4 | ||
|
|
b835478514 | ||
|
|
3950cf065e | ||
|
|
c7d2881d04 | ||
|
|
33febb669e | ||
|
|
6254db34cd | ||
|
|
e6df194201 | ||
|
|
2580a8ba78 | ||
|
|
6b58ef155e | ||
|
|
6f73e58b25 | ||
|
|
bc85a118ba | ||
|
|
70a5925b03 | ||
|
|
d266835820 | ||
|
|
952658ee63 | ||
|
|
27d723fba1 | ||
|
|
971715931b | ||
|
|
e3342bb731 | ||
|
|
506c8387fc | ||
|
|
9dedb588ba | ||
|
|
8b69958f19 | ||
|
|
1dd8a76113 | ||
|
|
cde1da6285 | ||
|
|
3665ebcb4b | ||
|
|
c31d70dd12 | ||
|
|
f217865435 | ||
|
|
501874980e | ||
|
|
e7b2b586b6 | ||
|
|
6a15aaf562 | ||
|
|
f5b680699f | ||
|
|
3c289a927d | ||
|
|
5805f242d0 | ||
|
|
4f0a4454ec | ||
|
|
d752b0f06a | ||
|
|
06d737ec9f | ||
|
|
04729794c8 | ||
|
|
68ea9ac4d0 | ||
|
|
1afa4971d1 | ||
|
|
5418f09864 | ||
|
|
aabeefb761 | ||
|
|
023c7da07b | ||
|
|
7af32d4c70 | ||
|
|
58724b1c5c | ||
|
|
f9494128bc | ||
|
|
cebff5e726 | ||
|
|
bcb6da18ef | ||
|
|
344c8f5c37 | ||
|
|
d3f450fca0 | ||
|
|
11a29b1610 | ||
|
|
54d67b679b | ||
|
|
64c480a8f1 | ||
|
|
cf6816064d | ||
|
|
59e402ac68 | ||
|
|
5042248ed7 | ||
|
|
56e3d165dd | ||
|
|
8ce144983d | ||
|
|
4afbfd11e0 | ||
|
|
31eb10590e | ||
|
|
358633e98d | ||
|
|
b7bc1113ac | ||
|
|
11bc4965e3 | ||
|
|
1ca0f34210 | ||
|
|
9cb6c3b039 | ||
|
|
b9be692e55 | ||
|
|
e0d86df5a5 | ||
|
|
2f80ebb3e8 | ||
|
|
83f47d3ca1 | ||
|
|
48df49d89c | ||
|
|
f16600ee86 | ||
|
|
7dbe05f6d8 | ||
|
|
2d461deb68 | ||
|
|
dbc7db981c | ||
|
|
5faf0189e8 | ||
|
|
4b55a928c9 | ||
|
|
d15c6d361b | ||
|
|
6b5d53ad39 | ||
|
|
b575aa6aac | ||
|
|
d131c29f3b | ||
|
|
22cbec1d95 | ||
|
|
b2b7b899a3 | ||
|
|
ac81eefe3f | ||
|
|
8d361205f6 | ||
|
|
352aa36823 | ||
|
|
336763f0c9 |
36
.coveragerc
Normal file
36
.coveragerc
Normal file
@@ -0,0 +1,36 @@
|
||||
# .coveragerc to control coverage.py
|
||||
[run]
|
||||
branch = True
|
||||
source = superset
|
||||
# omit = bad_file.py
|
||||
|
||||
[paths]
|
||||
source =
|
||||
superset/
|
||||
*/site-packages/
|
||||
|
||||
[report]
|
||||
# Regexes for lines to exclude from consideration
|
||||
exclude_lines =
|
||||
# Have to re-enable the standard pragma
|
||||
pragma: no cover
|
||||
|
||||
# Don't complain about missing debug-only code:
|
||||
def __repr__
|
||||
if self\.debug
|
||||
|
||||
# Don't complain if tests don't hit defensive assertion code:
|
||||
raise AssertionError
|
||||
raise NotImplementedError
|
||||
|
||||
# Don't complain if non-runnable code isn't run:
|
||||
if 0:
|
||||
if __name__ == .__main__.:
|
||||
|
||||
# Ignore importlib backport
|
||||
from importlib
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
||||
#fail_under = 100
|
||||
show_missing = True
|
||||
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -1,3 +1,4 @@
|
||||
docker/**/*.sh text eol=lf
|
||||
*.svg binary
|
||||
*.ipynb binary
|
||||
*.geojson binary
|
||||
|
||||
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
@@ -27,6 +27,8 @@ updates:
|
||||
- package-ecosystem: "uv"
|
||||
directory: "requirements/"
|
||||
open-pull-requests-limit: 10
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
labels:
|
||||
- uv
|
||||
- dependabot
|
||||
|
||||
2
.github/workflows/dependency-review.yml
vendored
2
.github/workflows/dependency-review.yml
vendored
@@ -48,6 +48,8 @@ jobs:
|
||||
allow-dependencies-licenses: pkg:npm/store2@2.14.2, pkg:npm/applitools/core, pkg:npm/applitools/core-base, pkg:npm/applitools/css-tree, pkg:npm/applitools/ec-client, pkg:npm/applitools/eg-socks5-proxy-server, pkg:npm/applitools/eyes, pkg:npm/applitools/eyes-cypress, pkg:npm/applitools/nml-client, pkg:npm/applitools/tunnel-client, pkg:npm/applitools/utils, pkg:npm/node-forge@1.3.1, pkg:npm/rgbcolor, pkg:npm/jszip@3.10.1
|
||||
|
||||
python-dependency-liccheck:
|
||||
# NOTE: Configuration for liccheck lives in our pyproject.yml.
|
||||
# You cannot use a liccheck.ini file in this workflow.
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: "Checkout Repository"
|
||||
|
||||
3
.github/workflows/docker.yml
vendored
3
.github/workflows/docker.yml
vendored
@@ -111,6 +111,9 @@ jobs:
|
||||
docker compose up superset-init --exit-code-from superset-init
|
||||
|
||||
docker-compose-image-tag:
|
||||
# Run this job only on pushes to master (not for PRs)
|
||||
# goal is to check that building the latest image works, not required for all PR pushes
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
|
||||
10
.github/workflows/pre-commit.yml
vendored
10
.github/workflows/pre-commit.yml
vendored
@@ -18,7 +18,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["current", "previous"]
|
||||
python-version: ["current", "previous", "next"]
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@v4
|
||||
@@ -53,6 +53,14 @@ jobs:
|
||||
cd docs
|
||||
yarn install --immutable
|
||||
|
||||
- name: Cache pre-commit environments
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.cache/pre-commit
|
||||
key: pre-commit-v2-${{ runner.os }}-py${{ matrix.python-version }}-${{ hashFiles('.pre-commit-config.yaml') }}
|
||||
restore-keys: |
|
||||
pre-commit-v2-${{ runner.os }}-py${{ matrix.python-version }}-
|
||||
|
||||
- name: pre-commit
|
||||
run: |
|
||||
set +e # Don't exit immediately on failure
|
||||
|
||||
2
.github/workflows/superset-docs-verify.yml
vendored
2
.github/workflows/superset-docs-verify.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# Do not bump this linkinator-action version without opening
|
||||
# an ASF Infra ticket to allow the new verison first!
|
||||
# an ASF Infra ticket to allow the new version first!
|
||||
- uses: JustinBeckwith/linkinator-action@v1.11.0
|
||||
continue-on-error: true # This will make the job advisory (non-blocking, no red X)
|
||||
with:
|
||||
|
||||
10
.github/workflows/superset-e2e.yml
vendored
10
.github/workflows/superset-e2e.yml
vendored
@@ -73,6 +73,7 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
|
||||
- name: Checkout using ref (workflow_dispatch)
|
||||
if: github.event_name == 'workflow_dispatch' && github.event.inputs.ref != ''
|
||||
uses: actions/checkout@v4
|
||||
@@ -137,9 +138,16 @@ jobs:
|
||||
NODE_OPTIONS: "--max-old-space-size=4096"
|
||||
with:
|
||||
run: cypress-run-all ${{ env.USE_DASHBOARD }} ${{ matrix.app_root }}
|
||||
- name: Set safe app root
|
||||
if: failure()
|
||||
id: set-safe-app-root
|
||||
run: |
|
||||
APP_ROOT="${{ matrix.app_root }}"
|
||||
SAFE_APP_ROOT=${APP_ROOT//\//_}
|
||||
echo "safe_app_root=$SAFE_APP_ROOT" >> $GITHUB_OUTPUT
|
||||
- name: Upload Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
path: ${{ github.workspace }}/superset-frontend/cypress-base/cypress/screenshots
|
||||
name: cypress-artifact-${{ github.run_id }}-${{ github.job }}-${{ matrix.browser }}-${{ matrix.parallel_id }}
|
||||
name: cypress-artifact-${{ github.run_id }}-${{ github.job }}-${{ matrix.browser }}-${{ matrix.parallel_id }}--${{ steps.set-safe-app-root.outputs.safe_app_root }}
|
||||
|
||||
27
.github/workflows/superset-frontend.yml
vendored
27
.github/workflows/superset-frontend.yml
vendored
@@ -26,6 +26,8 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
- name: Check for File Changes
|
||||
id: check
|
||||
@@ -39,6 +41,10 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
echo "git rev-parse --short HEAD"
|
||||
git rev-parse --short HEAD
|
||||
echo "git show -s --format=raw HEAD"
|
||||
git show -s --format=raw HEAD
|
||||
docker buildx build \
|
||||
-t $TAG \
|
||||
--cache-from=type=registry,ref=apache/superset-cache:3.10-slim-bookworm \
|
||||
@@ -115,24 +121,6 @@ jobs:
|
||||
files: merged-output/coverage-summary.json
|
||||
slug: apache/superset
|
||||
|
||||
core-cover:
|
||||
needs: frontend-build
|
||||
if: needs.frontend-build.outputs.should-run == 'true'
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Download Docker Image Artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: docker-image
|
||||
|
||||
- name: Load Docker Image
|
||||
run: docker load < docker-image.tar.gz
|
||||
|
||||
- name: superset-ui/core coverage
|
||||
run: |
|
||||
docker run --rm $TAG bash -c \
|
||||
"npm run core:cover"
|
||||
|
||||
lint-frontend:
|
||||
needs: frontend-build
|
||||
if: needs.frontend-build.outputs.should-run == 'true'
|
||||
@@ -144,7 +132,8 @@ jobs:
|
||||
name: docker-image
|
||||
|
||||
- name: Load Docker Image
|
||||
run: docker load < docker-image.tar.gz
|
||||
run: |
|
||||
docker load < docker-image.tar.gz
|
||||
|
||||
- name: eslint
|
||||
run: |
|
||||
|
||||
@@ -77,7 +77,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["current", "previous"]
|
||||
python-version: ["current", "previous", "next"]
|
||||
env:
|
||||
PYTHONPATH: ${{ github.workspace }}
|
||||
SUPERSET_CONFIG: tests.integration_tests.superset_test_config
|
||||
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["previous", "current"]
|
||||
python-version: ["previous", "current", "next"]
|
||||
env:
|
||||
PYTHONPATH: ${{ github.workspace }}
|
||||
steps:
|
||||
@@ -45,6 +45,13 @@ jobs:
|
||||
SUPERSET_SECRET_KEY: not-a-secret
|
||||
run: |
|
||||
pytest --durations-min=0.5 --cov-report= --cov=superset ./tests/common ./tests/unit_tests --cache-clear --maxfail=50
|
||||
- name: Python 100% coverage unit tests
|
||||
if: steps.check.outputs.python
|
||||
env:
|
||||
SUPERSET_TESTENV: true
|
||||
SUPERSET_SECRET_KEY: not-a-secret
|
||||
run: |
|
||||
pytest --durations-min=0.5 --cov-report= --cov=superset/sql/ ./tests/unit_tests/sql/ --cache-clear --cov-fail-under=100
|
||||
- name: Upload code coverage
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
|
||||
@@ -58,7 +58,7 @@ repos:
|
||||
- id: prettier
|
||||
additional_dependencies:
|
||||
- prettier@3.5.3
|
||||
args: ["--ignore-path=./superset-frontend/.prettierignore"]
|
||||
args: ["--ignore-path=./superset-frontend/.prettierignore", "--exclude", "site-packages"]
|
||||
files: "superset-frontend"
|
||||
- repo: local
|
||||
hooks:
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
######################################################################
|
||||
# Node stage to deal with static asset construction
|
||||
######################################################################
|
||||
ARG PY_VER=3.11.11-slim-bookworm
|
||||
ARG PY_VER=3.11.12-slim-bookworm
|
||||
|
||||
# If BUILDPLATFORM is null, set it to 'amd64' (or leave as is otherwise).
|
||||
ARG BUILDPLATFORM=${BUILDPLATFORM:-amd64}
|
||||
|
||||
@@ -103,7 +103,7 @@ Here are some of the major database solutions that are supported:
|
||||
|
||||
<p align="center">
|
||||
<img src="https://superset.apache.org/img/databases/redshift.png" alt="redshift" border="0" width="200"/>
|
||||
<img src="https://superset.apache.org/img/databases/google-biquery.png" alt="google-biquery" border="0" width="200"/>
|
||||
<img src="https://superset.apache.org/img/databases/google-biquery.png" alt="google-bigquery" border="0" width="200"/>
|
||||
<img src="https://superset.apache.org/img/databases/snowflake.png" alt="snowflake" border="0" width="200"/>
|
||||
<img src="https://superset.apache.org/img/databases/trino.png" alt="trino" border="0" width="150" />
|
||||
<img src="https://superset.apache.org/img/databases/presto.png" alt="presto" border="0" width="200"/>
|
||||
@@ -136,7 +136,7 @@ Here are some of the major database solutions that are supported:
|
||||
<img src="https://superset.apache.org/img/databases/starrocks.png" alt="starrocks" border="0" width="200" />
|
||||
<img src="https://superset.apache.org/img/databases/doris.png" alt="doris" border="0" width="200" />
|
||||
<img src="https://superset.apache.org/img/databases/oceanbase.svg" alt="oceanbase" border="0" width="220" />
|
||||
<img src="https://superset.apache.org/img/databases/sap-hana.png" alt="oceanbase" border="0" width="220" />
|
||||
<img src="https://superset.apache.org/img/databases/sap-hana.png" alt="sap-hana" border="0" width="220" />
|
||||
<img src="https://superset.apache.org/img/databases/denodo.png" alt="denodo" border="0" width="200" />
|
||||
<img src="https://superset.apache.org/img/databases/ydb.svg" alt="ydb" border="0" width="200" />
|
||||
<img src="https://superset.apache.org/img/databases/tdengine.png" alt="TDengine" border="0" width="200" />
|
||||
|
||||
@@ -43,6 +43,7 @@ Join our growing community!
|
||||
- [Cape Crypto](https://capecrypto.com)
|
||||
- [Capital Service S.A.](https://capitalservice.pl) [@pkonarzewski]
|
||||
- [Clark.de](https://clark.de/)
|
||||
- [Europace](https://europace.de)
|
||||
- [KarrotPay](https://www.daangnpay.com/)
|
||||
- [Remita](https://remita.net) [@mujibishola]
|
||||
- [Taveo](https://www.taveo.com) [@codek]
|
||||
@@ -104,6 +105,7 @@ Join our growing community!
|
||||
- [Formbricks](https://formbricks.com)
|
||||
- [Gavagai](https://gavagai.io) [@gavagai-corp]
|
||||
- [GfK Data Lab](https://www.gfk.com/home) [@mherr]
|
||||
- [HPE](https://www.hpe.com/in/en/home.html) [@anmol-hpe]
|
||||
- [Hydrolix](https://www.hydrolix.io/)
|
||||
- [Intercom](https://www.intercom.com/) [@kate-gallo]
|
||||
- [jampp](https://jampp.com/)
|
||||
|
||||
@@ -26,6 +26,7 @@ assists people when migrating to a new version.
|
||||
- [33116](https://github.com/apache/superset/pull/33116) In Echarts Series charts (e.g. Line, Area, Bar, etc.) charts, the `x_axis_sort_series` and `x_axis_sort_series_ascending` form data items have been renamed with `x_axis_sort` and `x_axis_sort_asc`.
|
||||
There's a migration added that can potentially affect a significant number of existing charts.
|
||||
- [32317](https://github.com/apache/superset/pull/32317) The horizontal filter bar feature is now out of testing/beta development and its feature flag `HORIZONTAL_FILTER_BAR` has been removed.
|
||||
- [31590](https://github.com/apache/superset/pull/31590) Marks the begining of intricate work around supporting dynamic Theming, and breaks support for [THEME_OVERRIDES](https://github.com/apache/superset/blob/732de4ac7fae88e29b7f123b6cbb2d7cd411b0e4/superset/config.py#L671) in favor of a new theming system based on AntD V5. Likely this will be in disrepair until settling over the 5.x lifecycle.
|
||||
- [31976](https://github.com/apache/superset/pull/31976) Removed the `DISABLE_LEGACY_DATASOURCE_EDITOR` feature flag. The previous value of the feature flag was `True` and now the feature is permanently removed.
|
||||
- [31959](https://github.com/apache/superset/pull/32000) Removes CSV_UPLOAD_MAX_SIZE config, use your web server to control file upload size.
|
||||
- [31959](https://github.com/apache/superset/pull/31959) Removes the following endpoints from data uploads: `/api/v1/database/<id>/<file type>_upload` and `/api/v1/database/<file type>_metadata`, in favour of new one (Details on the PR). And simplifies permissions.
|
||||
|
||||
@@ -65,8 +65,6 @@ services:
|
||||
superset-init:
|
||||
condition: service_completed_successfully
|
||||
volumes: *superset-volumes
|
||||
environment:
|
||||
SUPERSET_LOG_LEVEL: "${SUPERSET_LOG_LEVEL:-info}"
|
||||
|
||||
superset-init:
|
||||
image: *superset-image
|
||||
@@ -86,9 +84,6 @@ services:
|
||||
volumes: *superset-volumes
|
||||
healthcheck:
|
||||
disable: true
|
||||
environment:
|
||||
SUPERSET_LOAD_EXAMPLES: "${SUPERSET_LOAD_EXAMPLES:-yes}"
|
||||
SUPERSET_LOG_LEVEL: "${SUPERSET_LOG_LEVEL:-info}"
|
||||
|
||||
superset-worker:
|
||||
image: *superset-image
|
||||
@@ -111,8 +106,6 @@ services:
|
||||
"CMD-SHELL",
|
||||
"celery -A superset.tasks.celery_app:app inspect ping -d celery@$$HOSTNAME",
|
||||
]
|
||||
environment:
|
||||
SUPERSET_LOG_LEVEL: "${SUPERSET_LOG_LEVEL:-info}"
|
||||
|
||||
superset-worker-beat:
|
||||
image: *superset-image
|
||||
@@ -131,8 +124,6 @@ services:
|
||||
volumes: *superset-volumes
|
||||
healthcheck:
|
||||
disable: true
|
||||
environment:
|
||||
SUPERSET_LOG_LEVEL: "${SUPERSET_LOG_LEVEL:-info}"
|
||||
|
||||
volumes:
|
||||
superset_home:
|
||||
|
||||
@@ -71,8 +71,6 @@ services:
|
||||
superset-init:
|
||||
condition: service_completed_successfully
|
||||
volumes: *superset-volumes
|
||||
environment:
|
||||
SUPERSET_LOG_LEVEL: "${SUPERSET_LOG_LEVEL:-info}"
|
||||
|
||||
superset-init:
|
||||
container_name: superset_init
|
||||
@@ -93,9 +91,6 @@ services:
|
||||
volumes: *superset-volumes
|
||||
healthcheck:
|
||||
disable: true
|
||||
environment:
|
||||
SUPERSET_LOAD_EXAMPLES: "${SUPERSET_LOAD_EXAMPLES:-yes}"
|
||||
SUPERSET_LOG_LEVEL: "${SUPERSET_LOG_LEVEL:-info}"
|
||||
|
||||
superset-worker:
|
||||
build:
|
||||
@@ -119,8 +114,6 @@ services:
|
||||
"CMD-SHELL",
|
||||
"celery -A superset.tasks.celery_app:app inspect ping -d celery@$$HOSTNAME",
|
||||
]
|
||||
environment:
|
||||
SUPERSET_LOG_LEVEL: "${SUPERSET_LOG_LEVEL:-info}"
|
||||
|
||||
superset-worker-beat:
|
||||
build:
|
||||
@@ -140,8 +133,6 @@ services:
|
||||
volumes: *superset-volumes
|
||||
healthcheck:
|
||||
disable: true
|
||||
environment:
|
||||
SUPERSET_LOG_LEVEL: "${SUPERSET_LOG_LEVEL:-info}"
|
||||
|
||||
volumes:
|
||||
superset_home:
|
||||
|
||||
@@ -104,9 +104,6 @@ services:
|
||||
superset-init:
|
||||
condition: service_completed_successfully
|
||||
volumes: *superset-volumes
|
||||
environment:
|
||||
CYPRESS_CONFIG: "${CYPRESS_CONFIG:-}"
|
||||
SUPERSET_LOG_LEVEL: "${SUPERSET_LOG_LEVEL:-info}"
|
||||
|
||||
superset-websocket:
|
||||
container_name: superset_websocket
|
||||
@@ -158,10 +155,6 @@ services:
|
||||
condition: service_started
|
||||
user: *superset-user
|
||||
volumes: *superset-volumes
|
||||
environment:
|
||||
CYPRESS_CONFIG: "${CYPRESS_CONFIG:-}"
|
||||
SUPERSET_LOAD_EXAMPLES: "${SUPERSET_LOAD_EXAMPLES:-yes}"
|
||||
SUPERSET_LOG_LEVEL: "${SUPERSET_LOG_LEVEL:-info}"
|
||||
healthcheck:
|
||||
disable: true
|
||||
|
||||
@@ -206,8 +199,6 @@ services:
|
||||
required: false
|
||||
environment:
|
||||
CELERYD_CONCURRENCY: 2
|
||||
CYPRESS_CONFIG: "${CYPRESS_CONFIG:-}"
|
||||
SUPERSET_LOG_LEVEL: "${SUPERSET_LOG_LEVEL:-info}"
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
superset-init:
|
||||
@@ -239,9 +230,6 @@ services:
|
||||
volumes: *superset-volumes
|
||||
healthcheck:
|
||||
disable: true
|
||||
environment:
|
||||
CYPRESS_CONFIG: "${CYPRESS_CONFIG:-}"
|
||||
SUPERSET_LOG_LEVEL: "${SUPERSET_LOG_LEVEL:-info}"
|
||||
|
||||
superset-tests-worker:
|
||||
build:
|
||||
@@ -262,7 +250,6 @@ services:
|
||||
REDIS_RESULTS_DB: 3
|
||||
REDIS_HOST: localhost
|
||||
CELERYD_CONCURRENCY: 8
|
||||
SUPERSET_LOG_LEVEL: "${SUPERSET_LOG_LEVEL:-info}"
|
||||
network_mode: host
|
||||
depends_on:
|
||||
superset-init:
|
||||
|
||||
@@ -302,6 +302,15 @@ AUTH_USER_REGISTRATION = True
|
||||
AUTH_USER_REGISTRATION_ROLE = "Public"
|
||||
```
|
||||
|
||||
In case you want to assign the `Admin` role on new user registration, it can be assigned as follows:
|
||||
```python
|
||||
AUTH_USER_REGISTRATION_ROLE = "Admin"
|
||||
```
|
||||
If you encounter the [issue](https://github.com/apache/superset/issues/13243) of not being able to list users from the Superset main page settings, although a newly registered user has an `Admin` role, please re-run `superset init` to sync the required permissions. Below is the command to re-run `superset init` using docker compose.
|
||||
```
|
||||
docker-compose exec superset superset init
|
||||
```
|
||||
|
||||
Then, create a `CustomSsoSecurityManager` that extends `SupersetSecurityManager` and overrides
|
||||
`oauth_user_info`:
|
||||
|
||||
|
||||
@@ -72,7 +72,8 @@ are compatible with Superset.
|
||||
| [PostgreSQL](/docs/configuration/databases#postgres) | `pip install psycopg2` | `postgresql://<UserName>:<DBPassword>@<Database Host>/<Database Name>` |
|
||||
| [Presto](/docs/configuration/databases#presto) | `pip install pyhive` | `presto://{username}:{password}@{hostname}:{port}/{database}` |
|
||||
| [Rockset](/docs/configuration/databases#rockset) | `pip install rockset-sqlalchemy` | `rockset://<api_key>:@<api_server>` |
|
||||
| [SAP Hana](/docs/configuration/databases#hana) | `pip install hdbcli sqlalchemy-hana` or `pip install apache_superset[hana]` | `hana://{username}:{password}@{host}:{port}` |
|
||||
| [SAP Hana](/docs/configuration/databases#hana) | `pip install hdbcli sqlalchemy-hana` or `pip install apache_superset[hana]` | `hana://{username}:{password}@{host}:{port}` |
|
||||
| [SingleStore](/docs/configuration/databases#singlestore) | `pip install sqlalchemy-singlestoredb` | `singlestoredb://{username}:{password}@{host}:{port}/{database}` |
|
||||
| [StarRocks](/docs/configuration/databases#starrocks) | `pip install starrocks` | `starrocks://<User>:<Password>@<Host>:<Port>/<Catalog>.<Database>` |
|
||||
| [Snowflake](/docs/configuration/databases#snowflake) | `pip install snowflake-sqlalchemy` | `snowflake://{user}:{password}@{account}.{region}/{database}?role={role}&warehouse={warehouse}` |
|
||||
| SQLite | No additional library needed | `sqlite://path/to/file.db?check_same_thread=false` |
|
||||
@@ -1299,6 +1300,16 @@ You might have noticed that some special charecters are used in the above connec
|
||||
For more information about this check the [sqlalchemy documentation](https://docs.sqlalchemy.org/en/20/core/engines.html#escaping-special-characters-such-as-signs-in-passwords). Which says `When constructing a fully formed URL string to pass to create_engine(), special characters such as those that may be used in the user and password need to be URL encoded to be parsed correctly. This includes the @ sign.`
|
||||
:::
|
||||
|
||||
#### SingleStore
|
||||
|
||||
The recommended connector library for SingleStore is
|
||||
[sqlalchemy-singlestoredb](https://github.com/singlestore-labs/sqlalchemy-singlestoredb).
|
||||
|
||||
The expected connection string is formatted as follows:
|
||||
|
||||
```
|
||||
singlestoredb://{username}:{password}@{host}:{port}/{database}
|
||||
```
|
||||
|
||||
#### StarRocks
|
||||
|
||||
|
||||
@@ -250,6 +250,14 @@ Will be rendered as:
|
||||
SELECT * FROM users WHERE role IN ('admin', 'viewer')
|
||||
```
|
||||
|
||||
**Current User RLS Rules**
|
||||
|
||||
The `{{ current_user_rls_rules() }}` macro returns an array of RLS rules applied to the current dataset for the logged in user.
|
||||
|
||||
If you have caching enabled in your Superset configuration, then the list of RLS Rules will be used
|
||||
by Superset when calculating the cache key. A cache key is a unique identifier that determines if there's a
|
||||
cache hit in the future and Superset can retrieve cached data.
|
||||
|
||||
**Custom URL Parameters**
|
||||
|
||||
The `{{ url_param('custom_variable') }}` macro lets you define arbitrary URL
|
||||
|
||||
@@ -64,6 +64,56 @@ check the [supersetbot docker](https://github.com/apache-superset/supersetbot)
|
||||
subcommand and the [docker.yml](https://github.com/apache/superset/blob/master/.github/workflows/docker.yml)
|
||||
GitHub action.
|
||||
|
||||
## Building your own production Docker image
|
||||
|
||||
Every Superset deployment will require its own set of drivers depending on the data warehouse(s),
|
||||
etc. so we recommend that users build their own Docker image by extending the `lean` image.
|
||||
|
||||
Here's an example Dockerfile that does this. Follow the in-line comments to customize it for
|
||||
your desired Superset version and database drivers. The comments also note that a certain feature flag will
|
||||
have to be enabled in your config file.
|
||||
|
||||
You would build the image with `docker build -t mysuperset:latest .` or `docker build -t ourcompanysuperset:4.1.2 .`
|
||||
|
||||
```Dockerfile
|
||||
# change this to apache/superset:4.1.2 or whatever version you want to build from;
|
||||
# otherwise the default is the latest commit on GitHub master branch
|
||||
FROM apache/superset:master
|
||||
|
||||
USER root
|
||||
|
||||
# Set environment variable for Playwright
|
||||
ENV PLAYWRIGHT_BROWSERS_PATH=/usr/local/share/playwright-browsers
|
||||
|
||||
# Install packages using uv into the virtual environment
|
||||
# Superset started using uv after the 4.1 branch; if you are building from apache/superset:4.1.x,
|
||||
# replace the first two lines with RUN pip install \
|
||||
RUN . /app/.venv/bin/activate && \
|
||||
uv pip install \
|
||||
# install psycopg2 for using PostgreSQL metadata store - could be a MySQL package if using that backend:
|
||||
psycopg2-binary \
|
||||
# add the driver(s) for your data warehouse(s), in this example we're showing for Microsoft SQL Server:
|
||||
pymssql \
|
||||
# package needed for using single-sign on authentication:
|
||||
Authlib \
|
||||
# openpyxl to be able to upload Excel files
|
||||
openpyxl \
|
||||
# Pillow for Alerts & Reports to generate PDFs of dashboards
|
||||
Pillow \
|
||||
# install Playwright for taking screenshots for Alerts & Reports. This assumes the feature flag PLAYWRIGHT_REPORTS_AND_THUMBNAILS is enabled
|
||||
# That feature flag will default to True starting in 6.0.0
|
||||
# Playwright works only with Chrome.
|
||||
# If you are still using Selenium instead of Playwright, you would instead install here the selenium package and a headless browser & webdriver
|
||||
playwright \
|
||||
&& playwright install-deps \
|
||||
&& PLAYWRIGHT_BROWSERS_PATH=/usr/local/share/playwright-browsers playwright install chromium
|
||||
|
||||
# Switch back to the superset user
|
||||
USER superset
|
||||
|
||||
CMD ["/app/docker/entrypoints/run-server.sh"]
|
||||
```
|
||||
|
||||
## Key ARGs in Dockerfile
|
||||
|
||||
- `BUILD_TRANSLATIONS`: whether to build the translations into the image. For the
|
||||
|
||||
@@ -27,9 +27,7 @@ You will need to back up your metadata DB. That could mean backing up the servic
|
||||
|
||||
You will also need to extend the Superset docker image. The default `lean` images do not contain drivers needed to access your metadata database (Postgres or MySQL), nor to access your data warehouse, nor the headless browser needed for Alerts & Reports. You could run a `-dev` image while demoing Superset, which has some of this, but you'll still need to install the driver for your data warehouse. The `-dev` images run as root, which is not recommended for production.
|
||||
|
||||
Ideally you will build your own image of Superset that extends `lean`, adding what your deployment needs.
|
||||
|
||||
See [Docker Build Presets](/docs/installation/docker-builds/#build-presets) for more information about the different image versions you can extend.
|
||||
Ideally you will build your own image of Superset that extends `lean`, adding what your deployment needs. See [Building your own production Docker image](/docs/installation/docker-builds/#building-your-own-production-docker-image).
|
||||
|
||||
## [Kubernetes (K8s)](/docs/installation/kubernetes.mdx)
|
||||
|
||||
|
||||
@@ -2,6 +2,13 @@
|
||||
title: CVEs fixed by release
|
||||
sidebar_position: 2
|
||||
---
|
||||
#### Version 4.1.2
|
||||
|
||||
| CVE | Title | Affected |
|
||||
|:---------------|:-----------------------------------------------------------------------------------|---------:|
|
||||
| CVE-2025-27696 | Improper authorization leading to resource ownership takeover | < 4.1.2 |
|
||||
| CVE-2025-48912 | Improper authorization bypass on row level security via SQL Injection | < 4.1.2 |
|
||||
|
||||
#### Version 4.1.0
|
||||
|
||||
| CVE | Title | Affected |
|
||||
|
||||
@@ -26,10 +26,10 @@
|
||||
"@emotion/styled": "^10.0.27",
|
||||
"@saucelabs/theme-github-codeblock": "^0.3.0",
|
||||
"@superset-ui/style": "^0.14.23",
|
||||
"antd": "^5.24.5",
|
||||
"antd": "^5.25.1",
|
||||
"docusaurus-plugin-less": "^2.0.2",
|
||||
"less": "^4.3.0",
|
||||
"less-loader": "^11.0.0",
|
||||
"less-loader": "^12.3.0",
|
||||
"prism-react-renderer": "^2.4.1",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
@@ -39,17 +39,17 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/module-type-aliases": "^3.7.0",
|
||||
"@docusaurus/tsconfig": "^3.7.0",
|
||||
"@docusaurus/tsconfig": "^3.8.0",
|
||||
"@types/react": "^18.3.12",
|
||||
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
||||
"@typescript-eslint/parser": "^5.0.0",
|
||||
"eslint": "^8.0.0",
|
||||
"eslint-config-prettier": "^10.1.2",
|
||||
"eslint-config-prettier": "^10.1.5",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"prettier": "^2.0.0",
|
||||
"typescript": "~5.8.3",
|
||||
"webpack": "^5.99.7"
|
||||
"webpack": "^5.99.9"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
|
||||
164
docs/yarn.lock
164
docs/yarn.lock
@@ -1092,20 +1092,13 @@
|
||||
core-js-pure "^3.30.2"
|
||||
regenerator-runtime "^0.14.0"
|
||||
|
||||
"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.25.9", "@babel/runtime@^7.8.4":
|
||||
"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.3", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.7", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.6", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.4", "@babel/runtime@^7.24.7", "@babel/runtime@^7.24.8", "@babel/runtime@^7.25.7", "@babel/runtime@^7.25.9", "@babel/runtime@^7.26.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4":
|
||||
version "7.27.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.27.0.tgz#fbee7cf97c709518ecc1f590984481d5460d4762"
|
||||
integrity sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.14.0"
|
||||
|
||||
"@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.16.7", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.6", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.4", "@babel/runtime@^7.24.7", "@babel/runtime@^7.24.8", "@babel/runtime@^7.25.7", "@babel/runtime@^7.26.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2":
|
||||
version "7.26.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.10.tgz#a07b4d8fa27af131a633d7b3524db803eb4764c2"
|
||||
integrity sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.14.0"
|
||||
|
||||
"@babel/template@^7.25.9", "@babel/template@^7.26.9", "@babel/template@^7.27.0":
|
||||
version "7.27.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.0.tgz#b253e5406cc1df1c57dcd18f11760c2dbf40c0b4"
|
||||
@@ -1942,10 +1935,10 @@
|
||||
fs-extra "^11.1.1"
|
||||
tslib "^2.6.0"
|
||||
|
||||
"@docusaurus/tsconfig@^3.7.0":
|
||||
version "3.7.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/tsconfig/-/tsconfig-3.7.0.tgz#654dcc524e25b8809af0f1b0b42485c18c047ab5"
|
||||
integrity sha512-vRsyj3yUZCjscgfgcFYjIsTcAru/4h4YH2/XAE8Rs7wWdnng98PgWKvP5ovVc4rmRpRg2WChVW0uOy2xHDvDBQ==
|
||||
"@docusaurus/tsconfig@^3.8.0":
|
||||
version "3.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@docusaurus/tsconfig/-/tsconfig-3.8.0.tgz#ea7ee0917e1562cf0a6e95e049c42f1f61351f32"
|
||||
integrity sha512-utLl48nNjSYBoq47RKukZ9fPLEX3nJWThzrujb0ndQQ1jc/gh4RhTRaAqItH9nImnsgGKmLMnyoMBpfGmoop+w==
|
||||
|
||||
"@docusaurus/types@3.7.0":
|
||||
version "3.7.0"
|
||||
@@ -4186,10 +4179,10 @@ ansi-styles@^6.1.0:
|
||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5"
|
||||
integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==
|
||||
|
||||
antd@^5.24.5:
|
||||
version "5.24.5"
|
||||
resolved "https://registry.yarnpkg.com/antd/-/antd-5.24.5.tgz#b0976a113163888d1477f9e666c3c23352b098e9"
|
||||
integrity sha512-1lAv/G+9ewQanyoAo3JumQmIlVxwo5QwWGb6QCHYc40Cq0NxC/EzITcjsgq1PSaTUpLkKq8A2l7Fjtu47vqQBg==
|
||||
antd@^5.25.1:
|
||||
version "5.25.1"
|
||||
resolved "https://registry.yarnpkg.com/antd/-/antd-5.25.1.tgz#859b419a18d113492304ccd66c29074a71902241"
|
||||
integrity sha512-4KC7KuPCjr0z3Vuw9DsF+ceqJaPLbuUI3lOX1sY8ix25ceamp+P8yxOmk3Y2JHCD2ZAhq+5IQ/DTJRN2adWYKQ==
|
||||
dependencies:
|
||||
"@ant-design/colors" "^7.2.0"
|
||||
"@ant-design/cssinjs" "^1.23.0"
|
||||
@@ -4206,37 +4199,37 @@ antd@^5.24.5:
|
||||
classnames "^2.5.1"
|
||||
copy-to-clipboard "^3.3.3"
|
||||
dayjs "^1.11.11"
|
||||
rc-cascader "~3.33.1"
|
||||
rc-cascader "~3.34.0"
|
||||
rc-checkbox "~3.5.0"
|
||||
rc-collapse "~3.9.0"
|
||||
rc-dialog "~9.6.0"
|
||||
rc-drawer "~7.2.0"
|
||||
rc-dropdown "~4.2.1"
|
||||
rc-field-form "~2.7.0"
|
||||
rc-image "~7.11.1"
|
||||
rc-input "~1.7.3"
|
||||
rc-input-number "~9.4.0"
|
||||
rc-mentions "~2.19.1"
|
||||
rc-image "~7.12.0"
|
||||
rc-input "~1.8.0"
|
||||
rc-input-number "~9.5.0"
|
||||
rc-mentions "~2.20.0"
|
||||
rc-menu "~9.16.1"
|
||||
rc-motion "^2.9.5"
|
||||
rc-notification "~5.6.3"
|
||||
rc-notification "~5.6.4"
|
||||
rc-pagination "~5.1.0"
|
||||
rc-picker "~4.11.3"
|
||||
rc-progress "~4.0.0"
|
||||
rc-rate "~2.13.1"
|
||||
rc-resize-observer "^1.4.3"
|
||||
rc-segmented "~2.7.0"
|
||||
rc-select "~14.16.6"
|
||||
rc-select "~14.16.7"
|
||||
rc-slider "~11.1.8"
|
||||
rc-steps "~6.0.1"
|
||||
rc-switch "~4.1.0"
|
||||
rc-table "~7.50.4"
|
||||
rc-tabs "~15.5.1"
|
||||
rc-textarea "~1.9.0"
|
||||
rc-tabs "~15.6.1"
|
||||
rc-textarea "~1.10.0"
|
||||
rc-tooltip "~6.4.0"
|
||||
rc-tree "~5.13.1"
|
||||
rc-tree-select "~5.27.0"
|
||||
rc-upload "~4.8.1"
|
||||
rc-upload "~4.9.0"
|
||||
rc-util "^5.44.4"
|
||||
scroll-into-view-if-needed "^3.1.0"
|
||||
throttle-debounce "^5.0.2"
|
||||
@@ -5674,12 +5667,7 @@ data-view-byte-offset@^1.0.1:
|
||||
es-errors "^1.3.0"
|
||||
is-data-view "^1.0.1"
|
||||
|
||||
dayjs@^1.11.11:
|
||||
version "1.11.12"
|
||||
resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.12.tgz"
|
||||
integrity sha512-Rt2g+nTbLlDWZTwwrIXjy9MeiZmSDI375FvZs72ngxx8PDC6YXOeR3q5LAuPzjZQxhiWdRKac7RKV+YyQYfYIg==
|
||||
|
||||
dayjs@^1.11.13:
|
||||
dayjs@^1.11.11, dayjs@^1.11.13:
|
||||
version "1.11.13"
|
||||
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.13.tgz#92430b0139055c3ebb60150aa13e860a4b5a366c"
|
||||
integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==
|
||||
@@ -6259,10 +6247,10 @@ escape-string-regexp@^5.0.0:
|
||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8"
|
||||
integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==
|
||||
|
||||
eslint-config-prettier@^10.1.2:
|
||||
version "10.1.2"
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-10.1.2.tgz#31a4b393c40c4180202c27e829af43323bf85276"
|
||||
integrity sha512-Epgp/EofAUeEpIdZkW60MHKvPyru1ruQJxPL+WIycnaPApuseK0Zpkrh/FwL9oIpQvIhJwV7ptOy0DWUjTlCiA==
|
||||
eslint-config-prettier@^10.1.5:
|
||||
version "10.1.5"
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz#00c18d7225043b6fbce6a665697377998d453782"
|
||||
integrity sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==
|
||||
|
||||
eslint-plugin-prettier@^4.0.0:
|
||||
version "4.2.1"
|
||||
@@ -8223,10 +8211,10 @@ layout-base@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/layout-base/-/layout-base-2.0.1.tgz#d0337913586c90f9c2c075292069f5c2da5dd285"
|
||||
integrity sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==
|
||||
|
||||
less-loader@^11.0.0:
|
||||
version "11.1.4"
|
||||
resolved "https://registry.npmjs.org/less-loader/-/less-loader-11.1.4.tgz"
|
||||
integrity sha512-6/GrYaB6QcW6Vj+/9ZPgKKs6G10YZai/l/eJ4SLwbzqNTBsAqt5hSLVF47TgsiBxV1P6eAU0GYRH3YRuQU9V3A==
|
||||
less-loader@^12.3.0:
|
||||
version "12.3.0"
|
||||
resolved "https://registry.yarnpkg.com/less-loader/-/less-loader-12.3.0.tgz#d4a00361568be86a97da3df4f16954b0d4c15340"
|
||||
integrity sha512-0M6+uYulvYIWs52y0LqN4+QM9TqWAohYSNTo4htE8Z7Cn3G/qQMEmktfHmyJT23k+20kU9zHH2wrfFXkxNLtVw==
|
||||
|
||||
less@^4.3.0:
|
||||
version "4.3.0"
|
||||
@@ -10617,10 +10605,10 @@ raw-body@2.5.2:
|
||||
iconv-lite "0.4.24"
|
||||
unpipe "1.0.0"
|
||||
|
||||
rc-cascader@~3.33.1:
|
||||
version "3.33.1"
|
||||
resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-3.33.1.tgz#19e01462ef5ef51b723c1f562c7b9cde4691e7ee"
|
||||
integrity sha512-Kyl4EJ7ZfCBuidmZVieegcbFw0RcU5bHHSbtEdmuLYd0fYHCAiYKZ6zon7fWAVyC6rWWOOib0XKdTSf7ElC9rg==
|
||||
rc-cascader@~3.34.0:
|
||||
version "3.34.0"
|
||||
resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-3.34.0.tgz#56f936ab6b1229bab7d558701ce9b9e96536582c"
|
||||
integrity sha512-KpXypcvju9ptjW9FaN2NFcA2QH9E9LHKq169Y0eWtH4e/wHQ5Wh5qZakAgvb8EKZ736WZ3B0zLLOBsrsja5Dag==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.25.7"
|
||||
classnames "^2.3.1"
|
||||
@@ -10688,10 +10676,10 @@ rc-field-form@~2.7.0:
|
||||
"@rc-component/async-validator" "^5.0.3"
|
||||
rc-util "^5.32.2"
|
||||
|
||||
rc-image@~7.11.1:
|
||||
version "7.11.1"
|
||||
resolved "https://registry.yarnpkg.com/rc-image/-/rc-image-7.11.1.tgz#3ab290708dc053d3681de94186522e4e594f6772"
|
||||
integrity sha512-XuoWx4KUXg7hNy5mRTy1i8c8p3K8boWg6UajbHpDXS5AlRVucNfTi5YxTtPBTBzegxAZpvuLfh3emXFt6ybUdA==
|
||||
rc-image@~7.12.0:
|
||||
version "7.12.0"
|
||||
resolved "https://registry.yarnpkg.com/rc-image/-/rc-image-7.12.0.tgz#95e9314701e668217d113c1f29b4f01ac025cafe"
|
||||
integrity sha512-cZ3HTyyckPnNnUb9/DRqduqzLfrQRyi+CdHjdqgsyDpI3Ln5UX1kXnAhPBSJj9pVRzwRFgqkN7p9b6HBDjmu/Q==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.11.2"
|
||||
"@rc-component/portal" "^1.0.2"
|
||||
@@ -10700,37 +10688,37 @@ rc-image@~7.11.1:
|
||||
rc-motion "^2.6.2"
|
||||
rc-util "^5.34.1"
|
||||
|
||||
rc-input-number@~9.4.0:
|
||||
version "9.4.0"
|
||||
resolved "https://registry.npmjs.org/rc-input-number/-/rc-input-number-9.4.0.tgz"
|
||||
integrity sha512-Tiy4DcXcFXAf9wDhN8aUAyMeCLHJUHA/VA/t7Hj8ZEx5ETvxG7MArDOSE6psbiSCo+vJPm4E3fGN710ITVn6GA==
|
||||
rc-input-number@~9.5.0:
|
||||
version "9.5.0"
|
||||
resolved "https://registry.yarnpkg.com/rc-input-number/-/rc-input-number-9.5.0.tgz#b47963d0f2cbd85ab2f1badfdc089a904c073f38"
|
||||
integrity sha512-bKaEvB5tHebUURAEXw35LDcnRZLq3x1k7GxfAqBMzmpHkDGzjAtnUL8y4y5N15rIFIg5IJgwr211jInl3cipag==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.10.1"
|
||||
"@rc-component/mini-decimal" "^1.0.1"
|
||||
classnames "^2.2.5"
|
||||
rc-input "~1.7.1"
|
||||
rc-input "~1.8.0"
|
||||
rc-util "^5.40.1"
|
||||
|
||||
rc-input@~1.7.1, rc-input@~1.7.3:
|
||||
version "1.7.3"
|
||||
resolved "https://registry.yarnpkg.com/rc-input/-/rc-input-1.7.3.tgz#cb334a17b93ce985bceb243b4c111a5ed641e0e3"
|
||||
integrity sha512-A5w4egJq8+4JzlQ55FfQjDnPvOaAbzwC3VLOAdOytyek3TboSOP9qxN+Gifup+shVXfvecBLBbWBpWxmk02SWQ==
|
||||
rc-input@~1.8.0:
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/rc-input/-/rc-input-1.8.0.tgz#d2f4404befebf2fbdc28390d5494c302f74ae974"
|
||||
integrity sha512-KXvaTbX+7ha8a/k+eg6SYRVERK0NddX8QX7a7AnRvUa/rEH0CNMlpcBzBkhI0wp2C8C4HlMoYl8TImSN+fuHKA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.11.1"
|
||||
classnames "^2.2.1"
|
||||
rc-util "^5.18.1"
|
||||
|
||||
rc-mentions@~2.19.1:
|
||||
version "2.19.1"
|
||||
resolved "https://registry.npmjs.org/rc-mentions/-/rc-mentions-2.19.1.tgz"
|
||||
integrity sha512-KK3bAc/bPFI993J3necmaMXD2reZTzytZdlTvkeBbp50IGH1BDPDvxLdHDUrpQx2b2TGaVJsn+86BvYa03kGqA==
|
||||
rc-mentions@~2.20.0:
|
||||
version "2.20.0"
|
||||
resolved "https://registry.yarnpkg.com/rc-mentions/-/rc-mentions-2.20.0.tgz#3bbeac0352b02e0ce3e1244adb48701bb6903bf7"
|
||||
integrity sha512-w8HCMZEh3f0nR8ZEd466ATqmXFCMGMN5UFCzEUL0bM/nGw/wOS2GgRzKBcm19K++jDyuWCOJOdgcKGXU3fXfbQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.22.5"
|
||||
"@rc-component/trigger" "^2.0.0"
|
||||
classnames "^2.2.6"
|
||||
rc-input "~1.7.1"
|
||||
rc-input "~1.8.0"
|
||||
rc-menu "~9.16.0"
|
||||
rc-textarea "~1.9.0"
|
||||
rc-textarea "~1.10.0"
|
||||
rc-util "^5.34.1"
|
||||
|
||||
rc-menu@~9.16.0, rc-menu@~9.16.1:
|
||||
@@ -10754,10 +10742,10 @@ rc-motion@^2.0.0, rc-motion@^2.0.1, rc-motion@^2.3.0, rc-motion@^2.3.4, rc-motio
|
||||
classnames "^2.2.1"
|
||||
rc-util "^5.44.0"
|
||||
|
||||
rc-notification@~5.6.3:
|
||||
version "5.6.3"
|
||||
resolved "https://registry.npmjs.org/rc-notification/-/rc-notification-5.6.3.tgz"
|
||||
integrity sha512-42szwnn8VYQoT6GnjO00i1iwqV9D1TTMvxObWsuLwgl0TsOokzhkYiufdtQBsJMFjJravS1hfDKVMHLKLcPE4g==
|
||||
rc-notification@~5.6.4:
|
||||
version "5.6.4"
|
||||
resolved "https://registry.yarnpkg.com/rc-notification/-/rc-notification-5.6.4.tgz#ea89c39c13cd517fdfd97fe63f03376fabb78544"
|
||||
integrity sha512-KcS4O6B4qzM3KH7lkwOB7ooLPZ4b6J+VMmQgT51VZCeEcmghdeR4IrMcFq0LG+RPdnbe/ArT086tGM8Snimgiw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.10.1"
|
||||
classnames "2.x"
|
||||
@@ -10833,10 +10821,10 @@ rc-segmented@~2.7.0:
|
||||
rc-motion "^2.4.4"
|
||||
rc-util "^5.17.0"
|
||||
|
||||
rc-select@~14.16.2, rc-select@~14.16.6:
|
||||
version "14.16.6"
|
||||
resolved "https://registry.npmjs.org/rc-select/-/rc-select-14.16.6.tgz"
|
||||
integrity sha512-YPMtRPqfZWOm2XGTbx5/YVr1HT0vn//8QS77At0Gjb3Lv+Lbut0IORJPKLWu1hQ3u4GsA0SrDzs7nI8JG7Zmyg==
|
||||
rc-select@~14.16.2, rc-select@~14.16.7:
|
||||
version "14.16.8"
|
||||
resolved "https://registry.yarnpkg.com/rc-select/-/rc-select-14.16.8.tgz#78e6782f1ccc1f03d9003bc3effa4ed609d29a97"
|
||||
integrity sha512-NOV5BZa1wZrsdkKaiK7LHRuo5ZjZYMDxPP6/1+09+FB4KoNi8jcG1ZqLE3AVCxEsYMBe65OBx71wFoHRTP3LRg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.10.1"
|
||||
"@rc-component/trigger" "^2.1.1"
|
||||
@@ -10885,10 +10873,10 @@ rc-table@~7.50.4:
|
||||
rc-util "^5.44.3"
|
||||
rc-virtual-list "^3.14.2"
|
||||
|
||||
rc-tabs@~15.5.1:
|
||||
version "15.5.1"
|
||||
resolved "https://registry.npmjs.org/rc-tabs/-/rc-tabs-15.5.1.tgz"
|
||||
integrity sha512-yiWivLAjEo5d1v2xlseB2dQocsOhkoVSfo1krS8v8r+02K+TBUjSjXIf7dgyVSxp6wRIPv5pMi5hanNUlQMgUA==
|
||||
rc-tabs@~15.6.1:
|
||||
version "15.6.1"
|
||||
resolved "https://registry.yarnpkg.com/rc-tabs/-/rc-tabs-15.6.1.tgz#f0b6c65384dfa09a64eb539e86a0667c7a650708"
|
||||
integrity sha512-/HzDV1VqOsUWyuC0c6AkxVYFjvx9+rFPKZ32ejxX0Uc7QCzcEjTA9/xMgv4HemPKwzBNX8KhGVbbumDjnj92aA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.11.2"
|
||||
classnames "2.x"
|
||||
@@ -10898,14 +10886,14 @@ rc-tabs@~15.5.1:
|
||||
rc-resize-observer "^1.0.0"
|
||||
rc-util "^5.34.1"
|
||||
|
||||
rc-textarea@~1.9.0:
|
||||
version "1.9.0"
|
||||
resolved "https://registry.npmjs.org/rc-textarea/-/rc-textarea-1.9.0.tgz"
|
||||
integrity sha512-dQW/Bc/MriPBTugj2Kx9PMS5eXCCGn2cxoIaichjbNvOiARlaHdI99j4DTxLl/V8+PIfW06uFy7kjfUIDDKyxQ==
|
||||
rc-textarea@~1.10.0:
|
||||
version "1.10.0"
|
||||
resolved "https://registry.yarnpkg.com/rc-textarea/-/rc-textarea-1.10.0.tgz#f8f962ef83be0b8e35db97cf03dbfb86ddd9c46c"
|
||||
integrity sha512-ai9IkanNuyBS4x6sOL8qu/Ld40e6cEs6pgk93R+XLYg0mDSjNBGey6/ZpDs5+gNLD7urQ14po3V6Ck2dJLt9SA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.10.1"
|
||||
classnames "^2.2.1"
|
||||
rc-input "~1.7.1"
|
||||
rc-input "~1.8.0"
|
||||
rc-resize-observer "^1.0.0"
|
||||
rc-util "^5.27.0"
|
||||
|
||||
@@ -10941,10 +10929,10 @@ rc-tree@~5.13.0, rc-tree@~5.13.1:
|
||||
rc-util "^5.16.1"
|
||||
rc-virtual-list "^3.5.1"
|
||||
|
||||
rc-upload@~4.8.1:
|
||||
version "4.8.1"
|
||||
resolved "https://registry.npmjs.org/rc-upload/-/rc-upload-4.8.1.tgz"
|
||||
integrity sha512-toEAhwl4hjLAI1u8/CgKWt30BR06ulPa4iGQSMvSXoHzO88gPCslxqV/mnn4gJU7PDoltGIC9Eh+wkeudqgHyw==
|
||||
rc-upload@~4.9.0:
|
||||
version "4.9.0"
|
||||
resolved "https://registry.yarnpkg.com/rc-upload/-/rc-upload-4.9.0.tgz#911963ab5a0b538c743765371c05e2de9e3f5436"
|
||||
integrity sha512-pAzlPnyiFn1GCtEybEG2m9nXNzQyWXqWV2xFYCmDxjN9HzyjS5Pz2F+pbNdYw8mMJsixLEKLG0wVy9vOGxJMJA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.18.3"
|
||||
classnames "^2.2.5"
|
||||
@@ -13045,10 +13033,10 @@ webpack-sources@^3.2.3:
|
||||
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
|
||||
integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
|
||||
|
||||
webpack@^5.88.1, webpack@^5.95.0, webpack@^5.99.7:
|
||||
version "5.99.7"
|
||||
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.99.7.tgz#60201c1ca66da046b07d006c2f6e0cc5e8a7bdba"
|
||||
integrity sha512-CNqKBRMQjwcmKR0idID5va1qlhrqVUKpovi+Ec79ksW8ux7iS1+A6VqzfZXgVYCFRKl7XL5ap3ZoMpwBJxcg0w==
|
||||
webpack@^5.88.1, webpack@^5.95.0, webpack@^5.99.9:
|
||||
version "5.99.9"
|
||||
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.99.9.tgz#d7de799ec17d0cce3c83b70744b4aedb537d8247"
|
||||
integrity sha512-brOPwM3JnmOa+7kd3NsmOUOwbDAj8FT9xDsG3IW0MgbN9yZV7Oi/s/+MNQ/EcSMqw7qfoRyXPoeEWT8zLVdVGg==
|
||||
dependencies:
|
||||
"@types/eslint-scope" "^3.7.7"
|
||||
"@types/estree" "^1.0.6"
|
||||
|
||||
@@ -32,6 +32,7 @@ authors = [
|
||||
classifiers = [
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
]
|
||||
dependencies = [
|
||||
"backoff>=1.8.0",
|
||||
@@ -44,7 +45,7 @@ dependencies = [
|
||||
"cryptography>=42.0.4, <45.0.0",
|
||||
"deprecation>=2.1.0, <2.2.0",
|
||||
"flask>=2.2.5, <3.0.0",
|
||||
"flask-appbuilder>=4.6.3, <5.0.0",
|
||||
"flask-appbuilder>=4.7.0, <5.0.0",
|
||||
"flask-caching>=2.1.0, <3",
|
||||
"flask-compress>=1.13, <2.0",
|
||||
"flask-talisman>=1.0.0, <2.0",
|
||||
@@ -66,7 +67,7 @@ dependencies = [
|
||||
"markdown>=3.0",
|
||||
"msgpack>=1.0.0, <1.1",
|
||||
"nh3>=0.2.11, <0.3",
|
||||
"numpy>1.23.5, <2",
|
||||
"numpy>1.23.5, <2.3",
|
||||
"packaging",
|
||||
# --------------------------
|
||||
# pandas and related (wanting pandas[performance] without numba as it's 100+MB and not needed)
|
||||
@@ -93,8 +94,8 @@ dependencies = [
|
||||
"sqlalchemy>=1.4, <2",
|
||||
"sqlalchemy-utils>=0.38.3, <0.39",
|
||||
"sqlglot>=26.1.3, <27",
|
||||
"sqlparse>=0.5.0",
|
||||
"tabulate>=0.8.9, <0.9",
|
||||
# newer pandas needs 0.9+
|
||||
"tabulate>=0.9.0, <1.0",
|
||||
"typing-extensions>=4, <5",
|
||||
"waitress; sys_platform == 'win32'",
|
||||
"wtforms>=2.3.3, <4",
|
||||
@@ -166,6 +167,7 @@ prophet = ["prophet>=1.1.5, <2"]
|
||||
redshift = ["sqlalchemy-redshift>=0.8.1, <0.9"]
|
||||
rockset = ["rockset-sqlalchemy>=0.0.1, <1"]
|
||||
shillelagh = ["shillelagh[all]>=1.2.18, <2"]
|
||||
singlestore = ["sqlalchemy-singlestoredb>=1.1.1, <2"]
|
||||
snowflake = ["snowflake-sqlalchemy>=1.2.4, <2"]
|
||||
spark = [
|
||||
"pyhive[hive]>=0.6.5;python_version<'3.11'",
|
||||
@@ -216,7 +218,7 @@ combine_as_imports = true
|
||||
include_trailing_comma = true
|
||||
line_length = 88
|
||||
known_first_party = "superset"
|
||||
known_third_party = "alembic, apispec, backoff, celery, click, colorama, cron_descriptor, croniter, cryptography, dateutil, deprecation, flask, flask_appbuilder, flask_babel, flask_caching, flask_compress, flask_jwt_extended, flask_login, flask_migrate, flask_sqlalchemy, flask_talisman, flask_testing, flask_wtf, freezegun, geohash, geopy, holidays, humanize, isodate, jinja2, jwt, markdown, markupsafe, marshmallow, msgpack, nh3, numpy, pandas, parameterized, parsedatetime, pgsanity, polyline, prison, progress, pyarrow, sqlalchemy_bigquery, pyhive, pyparsing, pytest, pytest_mock, pytz, redis, requests, selenium, setuptools, shillelagh, simplejson, slack, sqlalchemy, sqlalchemy_utils, sqlparse, typing_extensions, urllib3, werkzeug, wtforms, wtforms_json, yaml"
|
||||
known_third_party = "alembic, apispec, backoff, celery, click, colorama, cron_descriptor, croniter, cryptography, dateutil, deprecation, flask, flask_appbuilder, flask_babel, flask_caching, flask_compress, flask_jwt_extended, flask_login, flask_migrate, flask_sqlalchemy, flask_talisman, flask_testing, flask_wtf, freezegun, geohash, geopy, holidays, humanize, isodate, jinja2, jwt, markdown, markupsafe, marshmallow, msgpack, nh3, numpy, pandas, parameterized, parsedatetime, pgsanity, polyline, prison, progress, pyarrow, sqlalchemy_bigquery, pyhive, pyparsing, pytest, pytest_mock, pytz, redis, requests, selenium, setuptools, shillelagh, simplejson, slack, sqlalchemy, sqlalchemy_utils, typing_extensions, urllib3, werkzeug, wtforms, wtforms_json, yaml"
|
||||
multi_line_output = 3
|
||||
order_by_type = false
|
||||
|
||||
@@ -240,6 +242,12 @@ disallow_untyped_calls = false
|
||||
disallow_untyped_defs = false
|
||||
disable_error_code = "annotation-unchecked"
|
||||
|
||||
# TODO: remove this once cryptography is fixed, introduced in cryptography 44.0.3
|
||||
[[tool.mypy.overrides]]
|
||||
module = "cryptography.*"
|
||||
ignore_errors = true
|
||||
follow_imports = "skip"
|
||||
|
||||
[tool.ruff]
|
||||
# Exclude a variety of commonly ignored directories.
|
||||
exclude = [
|
||||
@@ -272,7 +280,6 @@ exclude = [
|
||||
"venv",
|
||||
]
|
||||
|
||||
|
||||
# Same as Black.
|
||||
line-length = 88
|
||||
indent-width = 4
|
||||
@@ -367,6 +374,7 @@ docstring-code-line-length = "dynamic"
|
||||
requirement_txt_file = "requirements/base.txt"
|
||||
authorized_licenses = [
|
||||
"academic free license (afl)",
|
||||
"any-osi",
|
||||
"apache license 2.0",
|
||||
"apache software",
|
||||
"apache software, bsd",
|
||||
@@ -380,6 +388,7 @@ authorized_licenses = [
|
||||
"osi approved",
|
||||
"psf-2.0",
|
||||
"python software foundation",
|
||||
"simplified bsd",
|
||||
"the unlicense (unlicense)",
|
||||
"the unlicense",
|
||||
]
|
||||
|
||||
13
requirements/README.md
Normal file
13
requirements/README.md
Normal file
@@ -0,0 +1,13 @@
|
||||
## Python dependency logic
|
||||
|
||||
In this folder, the `.in` files, in conjunction with the `../pyproject.toml` file (in the root of the repo) are used to generate the pinned requirements as `.txt` files.
|
||||
|
||||
To alter the pinned dependency, you can edit/alter the `.in` and `pyproject.toml` files, and then run the following command:
|
||||
|
||||
```bash
|
||||
./scripts/uv-pip-compile.sh
|
||||
```
|
||||
|
||||
This will generate the pinned requirements in the `.txt` files, which will be used in our CI/CD pipelines and in the Docker images.
|
||||
|
||||
We recommend to everyone in the community to use the pinned requirements in their local development environments, to ensure consistency across different environments, though we don't force requirements as part of our python package semantics to allow flexibility for users to install different versions of the dependencies if they wish.
|
||||
@@ -33,3 +33,6 @@ apispec>=6.0.0,<6.7.0
|
||||
# https://marshmallow-sqlalchemy.readthedocs.io/en/latest/changelog.html#id3
|
||||
# Opened this issue https://github.com/marshmallow-code/marshmallow-sqlalchemy/issues/665
|
||||
marshmallow-sqlalchemy>=1.3.0,<1.4.1
|
||||
|
||||
# needed for python 3.12 support
|
||||
openapi-schema-validator>=0.6.3
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv pip compile pyproject.toml requirements/base.in -o requirements/base.txt
|
||||
alembic==1.15.1
|
||||
alembic==1.15.2
|
||||
# via flask-migrate
|
||||
amqp==5.3.1
|
||||
# via kombu
|
||||
@@ -8,12 +8,10 @@ apispec==6.6.1
|
||||
# via
|
||||
# -r requirements/base.in
|
||||
# flask-appbuilder
|
||||
apsw==3.49.1.0
|
||||
apsw==3.49.2.0
|
||||
# via shillelagh
|
||||
async-timeout==4.0.3
|
||||
# via
|
||||
# -r requirements/base.in
|
||||
# redis
|
||||
# via -r requirements/base.in
|
||||
attrs==25.3.0
|
||||
# via
|
||||
# cattrs
|
||||
@@ -32,7 +30,7 @@ billiard==4.2.1
|
||||
# via celery
|
||||
blinker==1.9.0
|
||||
# via flask
|
||||
bottleneck==1.4.2
|
||||
bottleneck==1.5.0
|
||||
# via apache-superset (pyproject.toml)
|
||||
brotli==1.1.0
|
||||
# via flask-compress
|
||||
@@ -42,11 +40,11 @@ cachelib==0.13.0
|
||||
# flask-session
|
||||
cachetools==5.5.2
|
||||
# via google-auth
|
||||
cattrs==24.1.2
|
||||
cattrs==24.1.3
|
||||
# via requests-cache
|
||||
celery==5.5.2
|
||||
# via apache-superset (pyproject.toml)
|
||||
certifi==2025.1.31
|
||||
certifi==2025.4.26
|
||||
# via
|
||||
# requests
|
||||
# selenium
|
||||
@@ -54,9 +52,9 @@ cffi==1.17.1
|
||||
# via
|
||||
# cryptography
|
||||
# pynacl
|
||||
charset-normalizer==3.4.1
|
||||
charset-normalizer==3.4.2
|
||||
# via requests
|
||||
click==8.1.8
|
||||
click==8.2.0
|
||||
# via
|
||||
# apache-superset (pyproject.toml)
|
||||
# celery
|
||||
@@ -99,11 +97,6 @@ email-validator==2.2.0
|
||||
# via flask-appbuilder
|
||||
et-xmlfile==2.0.0
|
||||
# via openpyxl
|
||||
exceptiongroup==1.2.2
|
||||
# via
|
||||
# cattrs
|
||||
# trio
|
||||
# trio-websocket
|
||||
flask==2.3.3
|
||||
# via
|
||||
# apache-superset (pyproject.toml)
|
||||
@@ -118,7 +111,7 @@ flask==2.3.3
|
||||
# flask-session
|
||||
# flask-sqlalchemy
|
||||
# flask-wtf
|
||||
flask-appbuilder==4.6.3
|
||||
flask-appbuilder==4.7.0
|
||||
# via apache-superset (pyproject.toml)
|
||||
flask-babel==2.0.0
|
||||
# via flask-appbuilder
|
||||
@@ -152,13 +145,12 @@ geographiclib==2.0
|
||||
# via geopy
|
||||
geopy==2.4.1
|
||||
# via apache-superset (pyproject.toml)
|
||||
google-auth==2.38.0
|
||||
google-auth==2.40.1
|
||||
# via shillelagh
|
||||
greenlet==3.1.1
|
||||
# via
|
||||
# apache-superset (pyproject.toml)
|
||||
# shillelagh
|
||||
# sqlalchemy
|
||||
gunicorn==23.0.0
|
||||
# via apache-superset (pyproject.toml)
|
||||
h11==0.16.0
|
||||
@@ -174,6 +166,7 @@ idna==3.10
|
||||
# email-validator
|
||||
# requests
|
||||
# trio
|
||||
# url-normalize
|
||||
importlib-metadata==8.7.0
|
||||
# via apache-superset (pyproject.toml)
|
||||
isodate==0.7.2
|
||||
@@ -189,9 +182,13 @@ jinja2==3.1.6
|
||||
jsonpath-ng==1.7.0
|
||||
# via apache-superset (pyproject.toml)
|
||||
jsonschema==4.23.0
|
||||
# via flask-appbuilder
|
||||
jsonschema-specifications==2024.10.1
|
||||
# via jsonschema
|
||||
# via
|
||||
# flask-appbuilder
|
||||
# openapi-schema-validator
|
||||
jsonschema-specifications==2025.4.1
|
||||
# via
|
||||
# jsonschema
|
||||
# openapi-schema-validator
|
||||
kombu==5.5.3
|
||||
# via celery
|
||||
korean-lunar-calendar==0.3.1
|
||||
@@ -238,12 +235,16 @@ numpy==1.26.4
|
||||
# pandas
|
||||
odfpy==1.4.1
|
||||
# via pandas
|
||||
openapi-schema-validator==0.6.3
|
||||
# via -r requirements/base.in
|
||||
openpyxl==3.1.5
|
||||
# via pandas
|
||||
ordered-set==4.1.0
|
||||
# via flask-limiter
|
||||
outcome==1.3.0.post0
|
||||
# via trio
|
||||
# via
|
||||
# trio
|
||||
# trio-websocket
|
||||
packaging==25.0
|
||||
# via
|
||||
# apache-superset (pyproject.toml)
|
||||
@@ -263,7 +264,7 @@ parsedatetime==2.6
|
||||
# via apache-superset (pyproject.toml)
|
||||
pgsanity==0.2.9
|
||||
# via apache-superset (pyproject.toml)
|
||||
platformdirs==4.3.7
|
||||
platformdirs==4.3.8
|
||||
# via requests-cache
|
||||
ply==3.11
|
||||
# via jsonpath-ng
|
||||
@@ -279,7 +280,7 @@ pyasn1==0.6.1
|
||||
# via
|
||||
# pyasn1-modules
|
||||
# rsa
|
||||
pyasn1-modules==0.4.1
|
||||
pyasn1-modules==0.4.2
|
||||
# via google-auth
|
||||
pycparser==2.22
|
||||
# via cffi
|
||||
@@ -334,15 +335,17 @@ requests==2.32.3
|
||||
# shillelagh
|
||||
requests-cache==1.2.1
|
||||
# via shillelagh
|
||||
rfc3339-validator==0.1.4
|
||||
# via openapi-schema-validator
|
||||
rich==13.9.4
|
||||
# via flask-limiter
|
||||
rpds-py==0.23.1
|
||||
rpds-py==0.25.0
|
||||
# via
|
||||
# jsonschema
|
||||
# referencing
|
||||
rsa==4.9
|
||||
rsa==4.9.1
|
||||
# via google-auth
|
||||
selenium==4.27.1
|
||||
selenium==4.32.0
|
||||
# via apache-superset (pyproject.toml)
|
||||
shillelagh==1.3.5
|
||||
# via apache-superset (pyproject.toml)
|
||||
@@ -352,6 +355,7 @@ six==1.17.0
|
||||
# via
|
||||
# prison
|
||||
# python-dateutil
|
||||
# rfc3339-validator
|
||||
# url-normalize
|
||||
# wtforms-json
|
||||
slack-sdk==3.35.0
|
||||
@@ -373,36 +377,32 @@ sqlalchemy-utils==0.38.3
|
||||
# via
|
||||
# apache-superset (pyproject.toml)
|
||||
# flask-appbuilder
|
||||
sqlglot==26.16.4
|
||||
# via apache-superset (pyproject.toml)
|
||||
sqlparse==0.5.3
|
||||
sqlglot==26.17.1
|
||||
# via apache-superset (pyproject.toml)
|
||||
sshtunnel==0.4.0
|
||||
# via apache-superset (pyproject.toml)
|
||||
tabulate==0.8.10
|
||||
tabulate==0.9.0
|
||||
# via apache-superset (pyproject.toml)
|
||||
trio==0.28.0
|
||||
trio==0.30.0
|
||||
# via
|
||||
# selenium
|
||||
# trio-websocket
|
||||
trio-websocket==0.11.1
|
||||
trio-websocket==0.12.2
|
||||
# via selenium
|
||||
typing-extensions==4.12.2
|
||||
typing-extensions==4.13.2
|
||||
# via
|
||||
# apache-superset (pyproject.toml)
|
||||
# alembic
|
||||
# cattrs
|
||||
# limits
|
||||
# pyopenssl
|
||||
# referencing
|
||||
# rich
|
||||
# selenium
|
||||
# shillelagh
|
||||
tzdata==2025.2
|
||||
# via
|
||||
# kombu
|
||||
# pandas
|
||||
url-normalize==1.4.3
|
||||
url-normalize==2.2.1
|
||||
# via requests-cache
|
||||
urllib3==1.26.20
|
||||
# via
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# uv pip compile requirements/development.in -c requirements/base.txt -o requirements/development.txt
|
||||
-e .
|
||||
# via -r requirements/development.in
|
||||
alembic==1.15.1
|
||||
alembic==1.15.2
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# flask-migrate
|
||||
@@ -14,14 +14,10 @@ apispec==6.6.1
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# flask-appbuilder
|
||||
apsw==3.49.1.0
|
||||
apsw==3.49.2.0
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# shillelagh
|
||||
async-timeout==4.0.3
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# redis
|
||||
attrs==25.3.0
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
@@ -51,7 +47,7 @@ blinker==1.9.0
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# flask
|
||||
bottleneck==1.4.2
|
||||
bottleneck==1.5.0
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# apache-superset
|
||||
@@ -68,7 +64,7 @@ cachetools==5.5.2
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# google-auth
|
||||
cattrs==24.1.2
|
||||
cattrs==24.1.3
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# requests-cache
|
||||
@@ -76,7 +72,7 @@ celery==5.5.2
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# apache-superset
|
||||
certifi==2025.1.31
|
||||
certifi==2025.4.26
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# requests
|
||||
@@ -88,11 +84,11 @@ cffi==1.17.1
|
||||
# pynacl
|
||||
cfgv==3.4.0
|
||||
# via pre-commit
|
||||
charset-normalizer==3.4.1
|
||||
charset-normalizer==3.4.2
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# requests
|
||||
click==8.1.8
|
||||
click==8.2.0
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# apache-superset
|
||||
@@ -176,13 +172,6 @@ et-xmlfile==2.0.0
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# openpyxl
|
||||
exceptiongroup==1.2.2
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# cattrs
|
||||
# pytest
|
||||
# trio
|
||||
# trio-websocket
|
||||
filelock==3.12.2
|
||||
# via virtualenv
|
||||
flask==2.3.3
|
||||
@@ -202,7 +191,7 @@ flask==2.3.3
|
||||
# flask-sqlalchemy
|
||||
# flask-testing
|
||||
# flask-wtf
|
||||
flask-appbuilder==4.6.3
|
||||
flask-appbuilder==4.7.0
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# apache-superset
|
||||
@@ -280,7 +269,7 @@ google-api-core==2.23.0
|
||||
# google-cloud-core
|
||||
# pandas-gbq
|
||||
# sqlalchemy-bigquery
|
||||
google-auth==2.38.0
|
||||
google-auth==2.40.1
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# google-api-core
|
||||
@@ -318,7 +307,6 @@ greenlet==3.1.1
|
||||
# apache-superset
|
||||
# gevent
|
||||
# shillelagh
|
||||
# sqlalchemy
|
||||
grpcio==1.71.0
|
||||
# via
|
||||
# apache-superset
|
||||
@@ -355,6 +343,7 @@ idna==3.10
|
||||
# email-validator
|
||||
# requests
|
||||
# trio
|
||||
# url-normalize
|
||||
importlib-metadata==8.7.0
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
@@ -389,7 +378,7 @@ jsonschema==4.23.0
|
||||
# openapi-spec-validator
|
||||
jsonschema-path==0.3.4
|
||||
# via openapi-spec-validator
|
||||
jsonschema-specifications==2024.10.1
|
||||
jsonschema-specifications==2025.4.1
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# jsonschema
|
||||
@@ -480,7 +469,9 @@ odfpy==1.4.1
|
||||
# -c requirements/base.txt
|
||||
# pandas
|
||||
openapi-schema-validator==0.6.3
|
||||
# via openapi-spec-validator
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# openapi-spec-validator
|
||||
openapi-spec-validator==0.7.1
|
||||
# via apache-superset
|
||||
openpyxl==3.1.5
|
||||
@@ -495,6 +486,7 @@ outcome==1.3.0.post0
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# trio
|
||||
# trio-websocket
|
||||
packaging==25.0
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
@@ -542,7 +534,7 @@ pillow==10.3.0
|
||||
# via
|
||||
# apache-superset
|
||||
# matplotlib
|
||||
platformdirs==4.3.7
|
||||
platformdirs==4.3.8
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# requests-cache
|
||||
@@ -598,7 +590,7 @@ pyasn1==0.6.1
|
||||
# pyasn1-modules
|
||||
# python-ldap
|
||||
# rsa
|
||||
pyasn1-modules==0.4.1
|
||||
pyasn1-modules==0.4.2
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# google-auth
|
||||
@@ -726,27 +718,29 @@ requests-cache==1.2.1
|
||||
requests-oauthlib==2.0.0
|
||||
# via google-auth-oauthlib
|
||||
rfc3339-validator==0.1.4
|
||||
# via openapi-schema-validator
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# openapi-schema-validator
|
||||
rich==13.9.4
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# flask-limiter
|
||||
rpds-py==0.23.1
|
||||
rpds-py==0.25.0
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# jsonschema
|
||||
# referencing
|
||||
rsa==4.9
|
||||
rsa==4.9.1
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# google-auth
|
||||
ruff==0.8.0
|
||||
# via apache-superset
|
||||
selenium==4.27.1
|
||||
selenium==4.32.0
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# apache-superset
|
||||
setuptools==75.6.0
|
||||
setuptools==80.7.1
|
||||
# via
|
||||
# nodeenv
|
||||
# pandas-gbq
|
||||
@@ -767,7 +761,6 @@ six==1.17.0
|
||||
# prison
|
||||
# python-dateutil
|
||||
# rfc3339-validator
|
||||
# url-normalize
|
||||
# wtforms-json
|
||||
slack-sdk==3.35.0
|
||||
# via
|
||||
@@ -799,55 +792,45 @@ sqlalchemy-utils==0.38.3
|
||||
# -c requirements/base.txt
|
||||
# apache-superset
|
||||
# flask-appbuilder
|
||||
sqlglot==26.16.4
|
||||
sqlglot==26.17.1
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# apache-superset
|
||||
sqloxide==0.1.51
|
||||
# via apache-superset
|
||||
sqlparse==0.5.3
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# apache-superset
|
||||
sshtunnel==0.4.0
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# apache-superset
|
||||
statsd==4.0.1
|
||||
# via apache-superset
|
||||
tabulate==0.8.10
|
||||
tabulate==0.9.0
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# apache-superset
|
||||
tomli==2.2.1
|
||||
# via
|
||||
# coverage
|
||||
# pytest
|
||||
tqdm==4.67.1
|
||||
# via
|
||||
# cmdstanpy
|
||||
# prophet
|
||||
trino==0.330.0
|
||||
# via apache-superset
|
||||
trio==0.28.0
|
||||
trio==0.30.0
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# selenium
|
||||
# trio-websocket
|
||||
trio-websocket==0.11.1
|
||||
trio-websocket==0.12.2
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# selenium
|
||||
typing-extensions==4.12.2
|
||||
typing-extensions==4.13.2
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# alembic
|
||||
# apache-superset
|
||||
# cattrs
|
||||
# limits
|
||||
# pyopenssl
|
||||
# referencing
|
||||
# rich
|
||||
# selenium
|
||||
# shillelagh
|
||||
tzdata==2025.2
|
||||
@@ -857,7 +840,7 @@ tzdata==2025.2
|
||||
# pandas
|
||||
tzlocal==5.2
|
||||
# via trino
|
||||
url-normalize==1.4.3
|
||||
url-normalize==2.2.1
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# requests-cache
|
||||
|
||||
@@ -116,8 +116,11 @@ Example `POST /security/guest_token` payload:
|
||||
}
|
||||
```
|
||||
|
||||
Alternatively, a guest token can be created directly in your app with a json like the following, and then signed
|
||||
with the secret set in configuration variable `GUEST_TOKEN_JWT_SECRET` (see configuration file config.py)
|
||||
Alternatively, a guest token can be created directly in your app without interacting with the Superset API.
|
||||
To do this, you should update the `GUEST_TOKEN_JWT_SECRET`
|
||||
in the Superset [config.py](https://github.com/apache/superset/blob/master/superset/config.py). Also set the
|
||||
`GUEST_TOKEN_JWT_AUDIENCE` variable that matches what is set for the `aud` in the JSON payload:
|
||||
|
||||
```
|
||||
{
|
||||
"user": {
|
||||
@@ -139,6 +142,13 @@ with the secret set in configuration variable `GUEST_TOKEN_JWT_SECRET` (see conf
|
||||
}
|
||||
```
|
||||
|
||||
In this example, the configuration file includes the following setting:
|
||||
|
||||
```python
|
||||
GUEST_TOKEN_JWT_AUDIENCE="superset"
|
||||
```
|
||||
|
||||
|
||||
### Sandbox iframe
|
||||
|
||||
The Embedded SDK creates an iframe with [sandbox](https://developer.mozilla.org/es/docs/Web/HTML/Element/iframe#sandbox) mode by default
|
||||
|
||||
@@ -89,6 +89,7 @@ export type EmbeddedDashboard = {
|
||||
callbackFn: ObserveDataMaskCallbackFn,
|
||||
) => void;
|
||||
getDataMask: () => Record<string, any>;
|
||||
setThemeConfig: (themeConfig: Record<string, any>) => void;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -245,6 +246,18 @@ export async function embedDashboard({
|
||||
ourPort.start();
|
||||
ourPort.defineMethod('observeDataMask', callbackFn);
|
||||
};
|
||||
// TODO: Add proper types once theming branch is merged
|
||||
const setThemeConfig = async (themeConfig: Record<string, any>): Promise<void> => {
|
||||
try {
|
||||
ourPort.emit('setThemeConfig', { themeConfig });
|
||||
log('Theme config sent successfully (or at least message dispatched)');
|
||||
} catch (error) {
|
||||
log(
|
||||
'Error sending theme config. Ensure the iframe side implements the "setThemeConfig" method.',
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
getScrollSize,
|
||||
@@ -253,5 +266,6 @@ export async function embedDashboard({
|
||||
getActiveTabs,
|
||||
observeDataMask,
|
||||
getDataMask,
|
||||
setThemeConfig
|
||||
};
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
coverage/**
|
||||
dist/*
|
||||
src/assets/images/*
|
||||
src/assets/stylesheets/*
|
||||
node_modules/*
|
||||
node_modules*/*
|
||||
vendor/*
|
||||
|
||||
@@ -52,7 +52,7 @@ const restrictedImportsRules = {
|
||||
message: 'Lodash Memoize is unsafe! Please use memoize-one instead',
|
||||
},
|
||||
'no-testing-library-react': {
|
||||
name: '@testing-library/react',
|
||||
name: '@superset-ui/core/spec',
|
||||
message: 'Please use spec/helpers/testing-library instead',
|
||||
},
|
||||
'no-testing-library-react-dom-utils': {
|
||||
@@ -63,10 +63,6 @@ const restrictedImportsRules = {
|
||||
name: 'antd',
|
||||
message: 'Please import Ant components from the index of src/components',
|
||||
},
|
||||
'no-antd-v5': {
|
||||
name: 'antd-v5',
|
||||
message: 'Please import Ant v5 components from the index of src/components',
|
||||
},
|
||||
'no-superset-theme': {
|
||||
name: '@superset-ui/core',
|
||||
importNames: ['supersetTheme'],
|
||||
@@ -101,6 +97,15 @@ module.exports = {
|
||||
// resolve modules from `/superset_frontend/node_modules` and `/superset_frontend`
|
||||
moduleDirectory: ['node_modules', '.'],
|
||||
},
|
||||
typescript: {
|
||||
alwaysTryTypes: true,
|
||||
project: [
|
||||
'./tsconfig.json',
|
||||
'./packages/superset-ui-core/tsconfig.json',
|
||||
'./packages/superset-ui-chart-controls/',
|
||||
'./plugins/*/tsconfig.json',
|
||||
],
|
||||
},
|
||||
},
|
||||
// only allow import from top level of module
|
||||
'import/core-modules': importCoreModules,
|
||||
@@ -295,9 +300,9 @@ module.exports = {
|
||||
'error',
|
||||
{
|
||||
paths: Object.values(restrictedImportsRules).filter(
|
||||
r => r.name !== 'antd-v5',
|
||||
r => r.name !== 'antd',
|
||||
),
|
||||
patterns: ['antd/*'],
|
||||
patterns: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -330,7 +335,9 @@ module.exports = {
|
||||
rules: {
|
||||
'import/no-extraneous-dependencies': [
|
||||
'error',
|
||||
{ devDependencies: true },
|
||||
{
|
||||
devDependencies: true,
|
||||
},
|
||||
],
|
||||
'no-only-tests/no-only-tests': 'error',
|
||||
'max-classes-per-file': 0,
|
||||
@@ -373,7 +380,7 @@ module.exports = {
|
||||
'fixtures.*',
|
||||
'cypress-base/cypress/**/*',
|
||||
'Stories.tsx',
|
||||
'packages/superset-ui-core/src/style/index.tsx',
|
||||
'packages/superset-ui-core/src/theme/index.tsx',
|
||||
],
|
||||
rules: {
|
||||
'theme-colors/no-literal-colors': 0,
|
||||
|
||||
@@ -17,31 +17,75 @@
|
||||
* under the License.
|
||||
*/
|
||||
import { withJsx } from '@mihkeleidast/storybook-addon-source';
|
||||
import { supersetTheme, ThemeProvider } from '@superset-ui/core';
|
||||
import { AntdThemeProvider } from '../src/components/AntdThemeProvider';
|
||||
import { themeObject, css, exampleThemes } from '@superset-ui/core';
|
||||
import { combineReducers, createStore, applyMiddleware, compose } from 'redux';
|
||||
import thunk from 'redux-thunk';
|
||||
import { Provider } from 'react-redux';
|
||||
import reducerIndex from 'spec/helpers/reducerIndex';
|
||||
import { GlobalStyles } from '../src/GlobalStyles';
|
||||
import { Global } from '@emotion/react';
|
||||
import { App, Layout, Space, Content } from 'antd';
|
||||
|
||||
import 'src/theme.ts';
|
||||
import './storybook.css';
|
||||
|
||||
export const GlobalStylesOverrides = () => (
|
||||
<Global
|
||||
styles={css`
|
||||
html,
|
||||
body,
|
||||
#storybook-root {
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
min-height: 100vh !important;
|
||||
}
|
||||
|
||||
.ant-app {
|
||||
min-height: 100vh !important;
|
||||
}
|
||||
`}
|
||||
/>
|
||||
);
|
||||
|
||||
const store = createStore(
|
||||
combineReducers(reducerIndex),
|
||||
{},
|
||||
compose(applyMiddleware(thunk)),
|
||||
);
|
||||
|
||||
const themeDecorator = Story => (
|
||||
<ThemeProvider theme={supersetTheme}>
|
||||
<AntdThemeProvider>
|
||||
<GlobalStyles />
|
||||
<Story />
|
||||
</AntdThemeProvider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
export const globalTypes = {
|
||||
theme: {
|
||||
name: 'Theme',
|
||||
description: 'Global theme for components',
|
||||
defaultValue: 'superset',
|
||||
toolbar: {
|
||||
icon: 'paintbrush',
|
||||
items: Object.keys(exampleThemes),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const themeDecorator = (Story, context) => {
|
||||
const themeKey = context.globals.theme || 'superset';
|
||||
themeObject.setConfig(exampleThemes[themeKey]);
|
||||
|
||||
return (
|
||||
<themeObject.SupersetThemeProvider>
|
||||
<App>
|
||||
<GlobalStylesOverrides />
|
||||
<Layout
|
||||
style={{
|
||||
minHeight: '100vh',
|
||||
width: '100%',
|
||||
padding: 24,
|
||||
backgroundColor: themeObject.theme.colorBgBase,
|
||||
}}
|
||||
>
|
||||
<Story {...context} />
|
||||
</Layout>
|
||||
</App>
|
||||
</themeObject.SupersetThemeProvider>
|
||||
);
|
||||
};
|
||||
|
||||
const providerDecorator = Story => (
|
||||
<Provider store={store}>
|
||||
@@ -81,5 +125,5 @@ export const parameters = {
|
||||
],
|
||||
},
|
||||
},
|
||||
controls: { expanded: true, sort: 'alpha' },
|
||||
controls: { expanded: true, sort: 'alpha', disableSaveFromUI: true },
|
||||
};
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
body {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { LOGIN } from 'cypress/utils/urls';
|
||||
|
||||
function interceptLogin() {
|
||||
cy.intercept('POST', '/login/').as('login');
|
||||
}
|
||||
|
||||
describe('Login view', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit(LOGIN);
|
||||
});
|
||||
|
||||
it('should load login page', () => {
|
||||
cy.getBySel('login-form').should('be.visible');
|
||||
cy.getBySel('username-input').should('be.visible');
|
||||
cy.getBySel('password-input').should('be.visible');
|
||||
cy.getBySel('login-button').should('be.visible');
|
||||
});
|
||||
|
||||
it('should redirect to login with incorrect username and password', () => {
|
||||
interceptLogin();
|
||||
cy.getBySel('login-form').should('be.visible');
|
||||
cy.getBySel('username-input').type('admin');
|
||||
cy.getBySel('password-input').type('wrongpassword');
|
||||
cy.getBySel('login-button').click();
|
||||
cy.wait('@login');
|
||||
cy.url().should('include', LOGIN);
|
||||
});
|
||||
|
||||
it('should login with correct username and password', () => {
|
||||
interceptLogin();
|
||||
cy.getBySel('login-form').should('be.visible');
|
||||
cy.getBySel('username-input').type('admin');
|
||||
cy.getBySel('password-input').type('general');
|
||||
cy.getBySel('login-button').click();
|
||||
cy.wait('@login');
|
||||
cy.getCookies().should('have.length', 1);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { REGISTER } from 'cypress/utils/urls';
|
||||
|
||||
describe('Register view', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit(REGISTER);
|
||||
});
|
||||
|
||||
it('should load register page', () => {
|
||||
cy.getBySel('register-form').should('be.visible');
|
||||
cy.getBySel('username-input').should('be.visible');
|
||||
cy.getBySel('first-name-input').should('be.visible');
|
||||
cy.getBySel('last-name-input').should('be.visible');
|
||||
cy.getBySel('email-input').should('be.visible');
|
||||
cy.getBySel('password-input').should('be.visible');
|
||||
cy.getBySel('confirm-password-input').should('be.visible');
|
||||
cy.getBySel('register-button').should('be.visible');
|
||||
});
|
||||
});
|
||||
@@ -35,12 +35,12 @@ function orderAlphabetical() {
|
||||
}
|
||||
|
||||
function openProperties() {
|
||||
cy.get('[aria-label="more"]').eq(1).click();
|
||||
cy.get('[aria-label="more"]').eq(0).click();
|
||||
cy.getBySel('chart-list-edit-option').click();
|
||||
}
|
||||
|
||||
function openMenu() {
|
||||
cy.get('[aria-label="more"]').eq(1).click();
|
||||
cy.get('[aria-label="more"]').eq(0).click();
|
||||
}
|
||||
|
||||
function confirmDelete() {
|
||||
@@ -81,12 +81,13 @@ describe('Charts list', () => {
|
||||
cy.wait('@get');
|
||||
});
|
||||
|
||||
it.only('should show the newly added dashboards in a tooltip', () => {
|
||||
it('should show the newly added dashboards in a tooltip', () => {
|
||||
interceptDashboardGet();
|
||||
visitSampleChartFromList('1 - Sample chart');
|
||||
saveChartToDashboard('1 - Sample dashboard');
|
||||
saveChartToDashboard('2 - Sample dashboard');
|
||||
saveChartToDashboard('3 - Sample dashboard');
|
||||
saveChartToDashboard('1 - Sample chart', '1 - Sample dashboard');
|
||||
saveChartToDashboard('1 - Sample chart', '2 - Sample dashboard');
|
||||
saveChartToDashboard('1 - Sample chart', '3 - Sample dashboard');
|
||||
saveChartToDashboard('1 - Sample chart', '4 - Sample dashboard');
|
||||
visitChartList();
|
||||
|
||||
cy.getBySel('count-crosslinks').should('be.visible');
|
||||
@@ -95,8 +96,6 @@ describe('Charts list', () => {
|
||||
|
||||
describe('list mode', () => {
|
||||
before(() => {
|
||||
cy.createSampleDashboards([0, 1, 2, 3]);
|
||||
cy.createSampleCharts([0]);
|
||||
visitChartList();
|
||||
setGridMode('list');
|
||||
});
|
||||
@@ -112,18 +111,10 @@ describe('Charts list', () => {
|
||||
cy.getBySel('sort-header').eq(7).contains('Actions');
|
||||
});
|
||||
|
||||
it('should sort correctly in list mode', () => {
|
||||
cy.getBySel('sort-header').eq(1).click();
|
||||
cy.getBySel('table-row').first().contains('Area Chart');
|
||||
cy.getBySel('sort-header').eq(1).click();
|
||||
cy.getBySel('table-row').first().contains("World's Population");
|
||||
cy.getBySel('sort-header').eq(1).click();
|
||||
});
|
||||
|
||||
it('should bulk select in list mode', () => {
|
||||
toggleBulkSelect();
|
||||
cy.get('#header-toggle-all').click();
|
||||
cy.get('[aria-label="checkbox-on"]').should('have.length', 26);
|
||||
cy.get('[aria-label="Select all"]').click();
|
||||
cy.get('input[type="checkbox"]:checked').should('have.length', 26);
|
||||
cy.getBySel('bulk-select-copy').contains('25 Selected');
|
||||
cy.getBySel('bulk-select-action')
|
||||
.should('have.length', 2)
|
||||
@@ -132,7 +123,7 @@ describe('Charts list', () => {
|
||||
expect($btns).to.contain('Export');
|
||||
});
|
||||
cy.getBySel('bulk-select-deselect-all').click();
|
||||
cy.get('[aria-label="checkbox-on"]').should('have.length', 0);
|
||||
cy.get('input[type="checkbox"]:checked').should('have.length', 0);
|
||||
cy.getBySel('bulk-select-copy').contains('0 Selected');
|
||||
cy.getBySel('bulk-select-action').should('not.exist');
|
||||
});
|
||||
@@ -164,11 +155,6 @@ describe('Charts list', () => {
|
||||
cy.getBySel('bulk-select-action').should('not.exist');
|
||||
});
|
||||
|
||||
it('should sort in card mode', () => {
|
||||
orderAlphabetical();
|
||||
cy.getBySel('styled-card').first().contains('% Rural');
|
||||
});
|
||||
|
||||
it('should preserve other filters when sorting', () => {
|
||||
cy.getBySel('styled-card').should('have.length', 25);
|
||||
setFilter('Type', 'Big Number');
|
||||
@@ -179,40 +165,12 @@ describe('Charts list', () => {
|
||||
|
||||
describe('common actions', () => {
|
||||
beforeEach(() => {
|
||||
cy.createSampleCharts([0, 1, 2, 3]);
|
||||
visitChartList();
|
||||
});
|
||||
|
||||
it('should allow to favorite/unfavorite', () => {
|
||||
cy.intercept({ url: `**/api/v1/chart/*/favorites/`, method: 'POST' }).as(
|
||||
'select',
|
||||
);
|
||||
cy.intercept({
|
||||
url: `**/api/v1/chart/*/favorites/`,
|
||||
method: 'DELETE',
|
||||
}).as('unselect');
|
||||
|
||||
setGridMode('card');
|
||||
orderAlphabetical();
|
||||
|
||||
cy.getBySel('styled-card').first().contains('% Rural');
|
||||
cy.getBySel('styled-card')
|
||||
.first()
|
||||
.find("[aria-label='favorite-unselected']")
|
||||
.click();
|
||||
cy.wait('@select');
|
||||
cy.getBySel('styled-card')
|
||||
.first()
|
||||
.find("[aria-label='favorite-selected']")
|
||||
.click();
|
||||
cy.wait('@unselect');
|
||||
cy.getBySel('styled-card')
|
||||
.first()
|
||||
.find("[aria-label='favorite-selected']")
|
||||
.should('not.exist');
|
||||
});
|
||||
|
||||
it('should bulk delete correctly', () => {
|
||||
cy.createSampleCharts([0, 1, 2, 3]);
|
||||
|
||||
interceptBulkDelete();
|
||||
toggleBulkSelect();
|
||||
|
||||
@@ -220,9 +178,10 @@ describe('Charts list', () => {
|
||||
setGridMode('card');
|
||||
orderAlphabetical();
|
||||
|
||||
cy.getBySel('styled-card').eq(1).contains('1 - Sample chart').click();
|
||||
cy.getBySel('styled-card').eq(2).contains('2 - Sample chart').click();
|
||||
cy.getBySel('bulk-select-action').eq(0).contains('Delete').click();
|
||||
cy.getBySel('skeleton-card').should('not.exist');
|
||||
cy.getBySel('styled-card').contains('1 - Sample chart').click();
|
||||
cy.getBySel('styled-card').contains('2 - Sample chart').click();
|
||||
cy.getBySel('bulk-select-action').contains('Delete').click();
|
||||
confirmDelete();
|
||||
cy.wait('@bulkDelete');
|
||||
cy.getBySel('styled-card')
|
||||
@@ -234,56 +193,71 @@ describe('Charts list', () => {
|
||||
|
||||
// bulk deletes in list-view
|
||||
setGridMode('list');
|
||||
cy.getBySel('table-row').eq(1).contains('3 - Sample chart');
|
||||
cy.getBySel('table-row').eq(2).contains('4 - Sample chart');
|
||||
cy.get('.loading').should('not.exist');
|
||||
cy.getBySel('table-row').contains('3 - Sample chart').should('exist');
|
||||
cy.getBySel('table-row').contains('4 - Sample chart').should('exist');
|
||||
cy.get('[data-test="table-row"] input[type="checkbox"]').eq(0).click();
|
||||
cy.get('[data-test="table-row"] input[type="checkbox"]').eq(1).click();
|
||||
cy.get('[data-test="table-row"] input[type="checkbox"]').eq(2).click();
|
||||
cy.getBySel('bulk-select-action').eq(0).contains('Delete').click();
|
||||
confirmDelete();
|
||||
cy.wait('@bulkDelete');
|
||||
cy.getBySel('table-row').eq(1).should('not.contain', '3 - Sample chart');
|
||||
cy.getBySel('table-row').eq(2).should('not.contain', '4 - Sample chart');
|
||||
cy.get('.loading').should('exist');
|
||||
cy.get('.loading').should('not.exist');
|
||||
cy.getBySel('table-row').eq(0).should('not.contain', '3 - Sample chart');
|
||||
cy.getBySel('table-row').eq(1).should('not.contain', '4 - Sample chart');
|
||||
});
|
||||
|
||||
it('should delete correctly', () => {
|
||||
it('should delete correctly in card mode', () => {
|
||||
cy.createSampleCharts([0, 1]);
|
||||
interceptDelete();
|
||||
|
||||
// deletes in card-view
|
||||
setGridMode('card');
|
||||
orderAlphabetical();
|
||||
|
||||
cy.getBySel('styled-card').eq(1).contains('1 - Sample chart');
|
||||
cy.getBySel('styled-card').contains('1 - Sample chart');
|
||||
openMenu();
|
||||
cy.getBySel('chart-list-delete-option').click();
|
||||
confirmDelete();
|
||||
cy.wait('@delete');
|
||||
cy.getBySel('styled-card')
|
||||
.eq(1)
|
||||
.should('not.contain', '1 - Sample chart');
|
||||
.contains('1 - Sample chart')
|
||||
.should('not.exist');
|
||||
});
|
||||
|
||||
// deletes in list-view
|
||||
setGridMode('list');
|
||||
cy.getBySel('table-row').eq(1).contains('2 - Sample chart');
|
||||
cy.getBySel('delete').eq(1).click();
|
||||
it('should delete correctly in list mode', () => {
|
||||
cy.createSampleCharts([2, 3]);
|
||||
interceptDelete();
|
||||
cy.getBySel('sort-header').contains('Name').click();
|
||||
|
||||
// Modal closes immediatly without this
|
||||
cy.wait(2000);
|
||||
|
||||
cy.getBySel('table-row').eq(0).contains('3 - Sample chart');
|
||||
cy.getBySel('delete').eq(0).click();
|
||||
confirmDelete();
|
||||
cy.wait('@delete');
|
||||
cy.getBySel('table-row').eq(1).should('not.contain', '2 - Sample chart');
|
||||
cy.get('.loading').should('exist');
|
||||
cy.get('.loading').should('not.exist');
|
||||
cy.getBySel('table-row').eq(0).should('not.contain', '3 - Sample chart');
|
||||
});
|
||||
|
||||
it('should edit correctly', () => {
|
||||
cy.createSampleCharts([0]);
|
||||
interceptUpdate();
|
||||
|
||||
// edits in card-view
|
||||
setGridMode('card');
|
||||
orderAlphabetical();
|
||||
cy.getBySel('styled-card').eq(1).contains('1 - Sample chart');
|
||||
cy.getBySel('skeleton-card').should('not.exist');
|
||||
cy.getBySel('styled-card').eq(0).contains('1 - Sample chart');
|
||||
|
||||
// change title
|
||||
openProperties();
|
||||
cy.getBySel('properties-modal-name-input').type(' | EDITED');
|
||||
cy.get('button:contains("Save")').click();
|
||||
cy.wait('@update');
|
||||
cy.getBySel('styled-card').eq(1).contains('1 - Sample chart | EDITED');
|
||||
cy.getBySel('styled-card').eq(0).contains('1 - Sample chart | EDITED');
|
||||
|
||||
// edits in list-view
|
||||
setGridMode('list');
|
||||
|
||||
@@ -47,12 +47,12 @@ describe.skip('Dashboard top-level controls', () => {
|
||||
// Solution: pause the network before clicking, assert, then unpause network.
|
||||
cy.get('[data-test="refresh-chart-menu-item"]').should(
|
||||
'have.class',
|
||||
'antd5-dropdown-menu-item-disabled',
|
||||
'ant-dropdown-menu-item-disabled',
|
||||
);
|
||||
waitForChartLoad(mapSpec);
|
||||
cy.get('[data-test="refresh-chart-menu-item"]').should(
|
||||
'not.have.class',
|
||||
'antd5-dropdown-menu-item-disabled',
|
||||
'ant-dropdown-menu-item-disabled',
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -65,7 +65,7 @@ describe.skip('Dashboard top-level controls', () => {
|
||||
cy.get('[aria-label="ellipsis"]').click();
|
||||
cy.get('[data-test="refresh-dashboard-menu-item"]').should(
|
||||
'not.have.class',
|
||||
'antd5-dropdown-menu-item-disabled',
|
||||
'ant-dropdown-menu-item-disabled',
|
||||
);
|
||||
|
||||
cy.get('[data-test="refresh-dashboard-menu-item"]').click({
|
||||
@@ -73,7 +73,7 @@ describe.skip('Dashboard top-level controls', () => {
|
||||
});
|
||||
cy.get('[data-test="refresh-dashboard-menu-item"]').should(
|
||||
'have.class',
|
||||
'antd5-dropdown-menu-item-disabled',
|
||||
'ant-dropdown-menu-item-disabled',
|
||||
);
|
||||
|
||||
// wait all charts force refreshed.
|
||||
@@ -94,7 +94,7 @@ describe.skip('Dashboard top-level controls', () => {
|
||||
cy.get('[aria-label="ellipsis"]').click();
|
||||
cy.get('[data-test="refresh-dashboard-menu-item"]').and(
|
||||
'not.have.class',
|
||||
'antd5-dropdown-menu-item-disabled',
|
||||
'ant-dropdown-menu-item-disabled',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -62,6 +62,6 @@ describe('Dashboard actions', () => {
|
||||
// Verify the color of the outlined star (gray)
|
||||
cy.get('@starIconOutlinedAfter')
|
||||
.should('have.css', 'color')
|
||||
.and('eq', 'rgb(178, 178, 178)');
|
||||
.and('eq', 'rgb(133, 133, 133)');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -54,14 +54,14 @@ const drillBy = (targetDrillByColumn: string, isLegacy = false) => {
|
||||
interceptV1ChartData();
|
||||
}
|
||||
|
||||
cy.get('.antd5-dropdown:not(.antd5-dropdown-hidden)')
|
||||
cy.get('.ant-dropdown:not(.ant-dropdown-hidden)')
|
||||
.should('be.visible')
|
||||
.find("[role='menu'] [role='menuitem']")
|
||||
.contains(/^Drill by$/)
|
||||
.trigger('mouseover', { force: true });
|
||||
|
||||
cy.get(
|
||||
'.antd5-dropdown-menu-submenu:not(.antd5-dropdown-menu-submenu-hidden) [data-test="drill-by-submenu"]',
|
||||
'.ant-dropdown-menu-submenu:not(.ant-dropdown-menu-submenu-hidden) [data-test="drill-by-submenu"]',
|
||||
)
|
||||
.should('be.visible')
|
||||
.find('[role="menuitem"]')
|
||||
|
||||
@@ -34,8 +34,8 @@ function openModalFromMenu(chartType: string) {
|
||||
cy.get(
|
||||
`[data-test-viz-type='${chartType}'] [aria-label='More Options']`,
|
||||
).click();
|
||||
cy.get('.antd5-dropdown')
|
||||
.not('.antd5-dropdown-hidden')
|
||||
cy.get('.ant-dropdown')
|
||||
.not('.ant-dropdown-hidden')
|
||||
.find("[role='menu'] [role='menuitem']")
|
||||
.eq(5)
|
||||
.should('contain', 'Drill to detail')
|
||||
@@ -46,8 +46,8 @@ function openModalFromMenu(chartType: string) {
|
||||
function drillToDetail(targetMenuItem: string) {
|
||||
interceptSamples();
|
||||
|
||||
cy.get('.antd5-dropdown')
|
||||
.not('.antd5-dropdown-hidden')
|
||||
cy.get('.ant-dropdown')
|
||||
.not('.ant-dropdown-hidden')
|
||||
.first()
|
||||
.find("[role='menu'] [role='menuitem']")
|
||||
.contains(new RegExp(`^${targetMenuItem}$`))
|
||||
@@ -61,14 +61,14 @@ function drillToDetail(targetMenuItem: string) {
|
||||
const drillToDetailBy = (targetDrill: string) => {
|
||||
interceptSamples();
|
||||
|
||||
cy.get('.antd5-dropdown:not(.antd5-dropdown-hidden)')
|
||||
cy.get('.ant-dropdown:not(.ant-dropdown-hidden)')
|
||||
.should('be.visible')
|
||||
.find("[role='menu'] [role='menuitem']")
|
||||
.contains(/^Drill to detail by$/)
|
||||
.trigger('mouseover', { force: true });
|
||||
|
||||
cy.get(
|
||||
'.antd5-dropdown-menu-submenu:not(.antd5-dropdown-menu-submenu-hidden) [data-test="drill-to-detail-by-submenu"]',
|
||||
'.ant-dropdown-menu-submenu:not(.ant-dropdown-menu-submenu-hidden) [data-test="drill-to-detail-by-submenu"]',
|
||||
)
|
||||
.should('be.visible')
|
||||
.find('[role="menuitem"]')
|
||||
@@ -121,7 +121,10 @@ function testTimeChart(vizType: string) {
|
||||
});
|
||||
}
|
||||
|
||||
describe('Drill to detail modal', () => {
|
||||
// TODO fix this test, it has issues with autoscrolling and the locked title
|
||||
// flakes intricately when the righClick is obstructed by the title.
|
||||
// Tried many option around scrollIntoView, force, etc. but no luck.
|
||||
describe.skip('Drill to detail modal', () => {
|
||||
beforeEach(() => {
|
||||
closeModal();
|
||||
});
|
||||
@@ -463,7 +466,7 @@ describe('Drill to detail modal', () => {
|
||||
});
|
||||
|
||||
// close the filter and test that data was reloaded
|
||||
cy.getBySel('filter-col').find("[aria-label='close']").click();
|
||||
cy.getBySel('filter-col').find("[aria-label='Close']").click();
|
||||
cy.wait('@samples');
|
||||
cy.getBySel('row-count-label').should('contain', '75.7k rows');
|
||||
cy.get('.ant-pagination-item-active').should('contain', '1');
|
||||
|
||||
@@ -17,7 +17,12 @@
|
||||
* under the License.
|
||||
*/
|
||||
import { SAMPLE_DASHBOARD_1, TABBED_DASHBOARD } from 'cypress/utils/urls';
|
||||
import { drag, resize, waitForChartLoad } from 'cypress/utils';
|
||||
import {
|
||||
drag,
|
||||
resize,
|
||||
setSelectSearchInput,
|
||||
waitForChartLoad,
|
||||
} from 'cypress/utils';
|
||||
import { edit } from 'brace';
|
||||
import {
|
||||
interceptExploreUpdate,
|
||||
@@ -34,21 +39,12 @@ function editDashboard() {
|
||||
cy.getBySel('edit-dashboard-button').click();
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
cy.getBySel('properties-modal-cancel-button').click({ force: true });
|
||||
}
|
||||
|
||||
function openProperties() {
|
||||
cy.get('body').then($body => {
|
||||
if ($body.find('[data-test="properties-modal-cancel-button"]').length) {
|
||||
closeModal();
|
||||
}
|
||||
cy.getBySel('actions-trigger').click({ force: true });
|
||||
cy.getBySel('header-actions-menu')
|
||||
.contains('Edit properties')
|
||||
.click({ force: true });
|
||||
cy.get('.antd5-modal-body').should('be.visible');
|
||||
});
|
||||
cy.getBySel('actions-trigger').click({ force: true });
|
||||
cy.getBySel('header-actions-menu')
|
||||
.contains('Edit properties')
|
||||
.click({ force: true });
|
||||
cy.get('.ant-modal-body').should('be.visible');
|
||||
}
|
||||
|
||||
function assertMetadata(text: string) {
|
||||
@@ -65,7 +61,7 @@ function assertMetadata(text: string) {
|
||||
}
|
||||
|
||||
function openAdvancedProperties() {
|
||||
cy.get('.antd5-modal-body')
|
||||
cy.get('.ant-modal-body')
|
||||
.contains('Advanced')
|
||||
.should('be.visible')
|
||||
.click({ force: true });
|
||||
@@ -150,12 +146,10 @@ function selectColorScheme(
|
||||
target = 'dashboard-edit-properties-form',
|
||||
) {
|
||||
cy.get(`[data-test="${target}"] input[aria-label="Select color scheme"]`)
|
||||
.first()
|
||||
.should('exist')
|
||||
.then($input => {
|
||||
cy.wrap($input).click({ force: true });
|
||||
cy.wrap($input).type(color.slice(0, 5), { force: true });
|
||||
setSelectSearchInput($input, color.slice(0, 5));
|
||||
});
|
||||
cy.getBySel(color).click({ force: true });
|
||||
}
|
||||
|
||||
function saveAndGo(dashboard = 'Tabbed Dashboard') {
|
||||
@@ -1095,7 +1089,7 @@ describe('Dashboard edit', () => {
|
||||
cy.allowConsoleErrors(['Error: A valid color scheme is required']);
|
||||
writeMetadata('{"color_scheme":"wrongcolorscheme"}');
|
||||
applyChanges();
|
||||
cy.get('.antd5-modal-body')
|
||||
cy.get('.ant-modal-body')
|
||||
.contains('A valid color scheme is required')
|
||||
.should('be.visible');
|
||||
});
|
||||
@@ -1130,7 +1124,7 @@ describe('Dashboard edit', () => {
|
||||
|
||||
it('should filter charts', () => {
|
||||
interceptCharts();
|
||||
cy.get('[role="checkbox"]').click();
|
||||
cy.get('input[type="checkbox"]').click();
|
||||
cy.getBySel('dashboard-charts-filter-search-input').type('Unicode');
|
||||
cy.wait('@filtering');
|
||||
cy.getBySel('chart-card')
|
||||
@@ -1140,8 +1134,8 @@ describe('Dashboard edit', () => {
|
||||
});
|
||||
|
||||
// TODO fix this test! This was the #1 flaky test as of 4/21/23 according to cypress dashboard.
|
||||
xit('should disable the Save button when undoing', () => {
|
||||
cy.get('[role="checkbox"]').click();
|
||||
it.skip('should disable the Save button when undoing', () => {
|
||||
cy.get('input[type="checkbox"]').click();
|
||||
dragComponent('Unicode Cloud', 'card-title', false);
|
||||
cy.getBySel('header-save-button').should('be.enabled');
|
||||
discardChanges();
|
||||
@@ -1155,13 +1149,13 @@ describe('Dashboard edit', () => {
|
||||
});
|
||||
|
||||
it('should add charts', () => {
|
||||
cy.get('[role="checkbox"]').click();
|
||||
cy.get('input[type="checkbox"]').click();
|
||||
dragComponent();
|
||||
cy.getBySel('dashboard-component-chart-holder').should('have.length', 1);
|
||||
});
|
||||
|
||||
it.skip('should remove added charts', () => {
|
||||
cy.get('[role="checkbox"]').click();
|
||||
cy.get('input[type="checkbox"]').click();
|
||||
dragComponent('Unicode Cloud');
|
||||
cy.getBySel('dashboard-component-chart-holder').should('have.length', 1);
|
||||
cy.getBySel('dashboard-delete-component-button').click();
|
||||
@@ -1204,7 +1198,7 @@ describe('Dashboard edit', () => {
|
||||
});
|
||||
|
||||
it('should save', () => {
|
||||
cy.get('[role="checkbox"]').click();
|
||||
cy.get('input[type="checkbox"]').click();
|
||||
dragComponent();
|
||||
cy.getBySel('header-save-button').should('be.enabled');
|
||||
saveChanges();
|
||||
|
||||
@@ -57,16 +57,16 @@ function setFilterBarOrientation(orientation: 'vertical' | 'horizontal') {
|
||||
.trigger('mouseover');
|
||||
|
||||
if (orientation === 'vertical') {
|
||||
cy.get('.antd5-dropdown-menu-item-selected')
|
||||
cy.get('.ant-dropdown-menu-item-selected')
|
||||
.contains('Horizontal (Top)')
|
||||
.should('exist');
|
||||
cy.get('.antd5-dropdown-menu-item').contains('Vertical (Left)').click();
|
||||
cy.get('.ant-dropdown-menu-item').contains('Vertical (Left)').click();
|
||||
cy.getBySel('dashboard-filters-panel').should('exist');
|
||||
} else {
|
||||
cy.get('.antd5-dropdown-menu-item-selected')
|
||||
cy.get('.ant-dropdown-menu-item-selected')
|
||||
.contains('Vertical (Left)')
|
||||
.should('exist');
|
||||
cy.get('.antd5-dropdown-menu-item').contains('Horizontal (Top)').click();
|
||||
cy.get('.ant-dropdown-menu-item').contains('Horizontal (Top)').click();
|
||||
cy.getBySel('loading-indicator').should('exist');
|
||||
cy.getBySel('filter-bar').should('exist');
|
||||
cy.getBySel('dashboard-filters-panel').should('not.exist');
|
||||
@@ -138,7 +138,7 @@ describe('Horizontal FilterBar', () => {
|
||||
cy.getBySel('dropdown-container-btn').should('not.exist');
|
||||
});
|
||||
|
||||
it('should show "more filters" and scroll', () => {
|
||||
it.only('should show "more filters" and scroll', () => {
|
||||
prepareDashboardFilters([
|
||||
{ name: 'test_1', column: 'country_name', datasetId: 2 },
|
||||
{ name: 'test_2', column: 'country_code', datasetId: 2 },
|
||||
@@ -157,11 +157,11 @@ describe('Horizontal FilterBar', () => {
|
||||
cy.get('.filter-item-wrapper').should('have.length', 3);
|
||||
openMoreFilters();
|
||||
cy.getBySel('form-item-value').should('have.length', 12);
|
||||
cy.getBySel('filter-control-name').contains('test_10').should('be.visible');
|
||||
cy.getBySel('filter-control-name').contains('test_3').should('be.visible');
|
||||
cy.getBySel('filter-control-name')
|
||||
.contains('test_12')
|
||||
.should('not.be.visible');
|
||||
cy.get('.antd5-popover-inner').scrollTo('bottom');
|
||||
cy.getBySel('filter-control-name').contains('test_12').scrollIntoView();
|
||||
cy.getBySel('filter-control-name').contains('test_12').should('be.visible');
|
||||
});
|
||||
|
||||
@@ -197,7 +197,7 @@ describe('Horizontal FilterBar', () => {
|
||||
applyNativeFilterValueWithIndex(8, testItems.filterDefaultValue);
|
||||
cy.get(nativeFilters.applyFilter).click({ force: true });
|
||||
cy.wait('@chart');
|
||||
cy.get('.antd5-scroll-number.antd5-badge-count').should(
|
||||
cy.get('.ant-scroll-number.ant-badge-count').should(
|
||||
'have.attr',
|
||||
'title',
|
||||
'1',
|
||||
|
||||
@@ -199,14 +199,16 @@ describe('Native filters', () => {
|
||||
.should('be.visible')
|
||||
.click();
|
||||
|
||||
cy.get('[data-test="range-filter-from-input"]').type('{selectall}5');
|
||||
cy.get('[data-test="range-filter-from-input"]').type('{selectall}40');
|
||||
|
||||
cy.get('[data-test="range-filter-to-input"]')
|
||||
.should('be.visible')
|
||||
.click();
|
||||
|
||||
cy.get('[data-test="range-filter-to-input"]').type('{selectall}50');
|
||||
cy.get(nativeFilters.applyFilter).click();
|
||||
cy.get(nativeFilters.applyFilter).click({
|
||||
force: true,
|
||||
});
|
||||
|
||||
// Assert that the URL contains 'native_filters'
|
||||
cy.url().then(u => {
|
||||
@@ -215,7 +217,7 @@ describe('Native filters', () => {
|
||||
|
||||
cy.get('[data-test="range-filter-from-input"]')
|
||||
.invoke('val')
|
||||
.should('equal', '5');
|
||||
.should('equal', '40');
|
||||
|
||||
// Assert that the "To" input has the correct value
|
||||
cy.get('[data-test="range-filter-to-input"]')
|
||||
|
||||
@@ -293,7 +293,11 @@ describe('Native filters', () => {
|
||||
|
||||
it('Verify setting options and tooltips for value filter', () => {
|
||||
enterNativeFilterEditModal(false);
|
||||
cy.contains('Filter value is required').should('be.visible').click();
|
||||
cy.contains('Filter value is required').scrollIntoView();
|
||||
|
||||
cy.contains('Filter value is required').should('be.visible').click({
|
||||
force: true,
|
||||
});
|
||||
checkNativeFilterTooltip(0, nativeFilterTooltips.preFilter);
|
||||
checkNativeFilterTooltip(1, nativeFilterTooltips.defaultValue);
|
||||
cy.get(nativeFilters.modal.container).should('be.visible');
|
||||
|
||||
@@ -18,7 +18,11 @@
|
||||
*/
|
||||
|
||||
import { dashboardView, nativeFilters } from 'cypress/support/directories';
|
||||
import { ChartSpec, waitForChartLoad } from 'cypress/utils';
|
||||
import {
|
||||
ChartSpec,
|
||||
setSelectSearchInput,
|
||||
waitForChartLoad,
|
||||
} from 'cypress/utils';
|
||||
|
||||
export const WORLD_HEALTH_CHARTS = [
|
||||
{ name: '% Rural', viz: 'world_map' },
|
||||
@@ -264,10 +268,11 @@ export function fillNativeFilterForm(
|
||||
dataset?: string,
|
||||
filterColumn?: string,
|
||||
) {
|
||||
cy.get(nativeFilters.filtersPanel.filterTypeInput)
|
||||
.find(nativeFilters.filtersPanel.filterTypeItem)
|
||||
.click({ multiple: true, force: true });
|
||||
cy.get(`[label="${type}"]`).click({ multiple: true, force: true });
|
||||
cy.get(nativeFilters.filtersPanel.filterTypeInput).within(() => {
|
||||
cy.get('input').then($input => {
|
||||
setSelectSearchInput($input, type);
|
||||
});
|
||||
});
|
||||
cy.get(nativeFilters.modal.container)
|
||||
.find(nativeFilters.filtersPanel.filterName)
|
||||
.last()
|
||||
@@ -280,31 +285,23 @@ export function fillNativeFilterForm(
|
||||
.find(nativeFilters.filtersPanel.filterName)
|
||||
.last()
|
||||
.type(name, { scrollBehavior: false, force: true });
|
||||
|
||||
if (dataset) {
|
||||
cy.get(nativeFilters.modal.container)
|
||||
.find(nativeFilters.filtersPanel.datasetName)
|
||||
.last()
|
||||
.click({ force: true, scrollBehavior: false });
|
||||
cy.get(nativeFilters.modal.container)
|
||||
.find(nativeFilters.filtersPanel.datasetName)
|
||||
.type(`${dataset}`, { scrollBehavior: false });
|
||||
cy.get('div[aria-label="Dataset"]').within(() => {
|
||||
cy.get('input').then($input => {
|
||||
setSelectSearchInput($input, dataset, true);
|
||||
});
|
||||
});
|
||||
cy.get(nativeFilters.silentLoading).should('not.exist');
|
||||
cy.get(`[label="${dataset}"]`).click({ multiple: true, force: true });
|
||||
}
|
||||
cy.get(nativeFilters.silentLoading).should('not.exist');
|
||||
|
||||
if (filterColumn) {
|
||||
cy.get(nativeFilters.filtersPanel.filterInfoInput)
|
||||
.last()
|
||||
.click({ force: true });
|
||||
cy.get(nativeFilters.filtersPanel.filterInfoInput)
|
||||
.last()
|
||||
.type(filterColumn);
|
||||
cy.get(nativeFilters.filtersPanel.inputDropdown)
|
||||
.should('be.visible', { timeout: 20000 })
|
||||
.last()
|
||||
.click();
|
||||
cy.get('div[aria-label="Column select"]').within(() => {
|
||||
cy.get('input').then($input => {
|
||||
setSelectSearchInput($input, filterColumn, true);
|
||||
});
|
||||
});
|
||||
}
|
||||
cy.get(nativeFilters.silentLoading).should('not.exist');
|
||||
}
|
||||
|
||||
/** ************************************************************************
|
||||
@@ -442,6 +439,9 @@ export function checkNativeFilterTooltip(index: number, value: string) {
|
||||
.eq(index)
|
||||
.trigger('mouseover');
|
||||
cy.contains(`${value}`);
|
||||
cy.get(nativeFilters.filterConfigurationSections.infoTooltip)
|
||||
.eq(index)
|
||||
.trigger('mouseout');
|
||||
}
|
||||
|
||||
/** ************************************************************************
|
||||
@@ -455,20 +455,20 @@ export function applyAdvancedTimeRangeFilterOnDashboard(
|
||||
startRange?: string,
|
||||
endRange?: string,
|
||||
) {
|
||||
cy.get('.control-label').contains('RANGE TYPE').should('be.visible');
|
||||
cy.get('.antd5-popover-content .ant-select-selector')
|
||||
cy.get('.control-label').contains('Range type').should('be.visible');
|
||||
cy.get('.ant-popover-content .ant-select-selector')
|
||||
.should('be.visible')
|
||||
.click();
|
||||
cy.get(`[label="Advanced"]`).should('be.visible').click();
|
||||
cy.get('.section-title').contains('Advanced Time Range').should('be.visible');
|
||||
if (startRange) {
|
||||
cy.get('.antd5-popover-inner-content')
|
||||
cy.get('.ant-popover-inner-content')
|
||||
.find('[class^=ant-input]')
|
||||
.first()
|
||||
.type(`${startRange}`);
|
||||
}
|
||||
if (endRange) {
|
||||
cy.get('.antd5-popover-inner-content')
|
||||
cy.get('.ant-popover-inner-content')
|
||||
.find('[class^=ant-input]')
|
||||
.last()
|
||||
.type(`${endRange}`);
|
||||
|
||||
@@ -79,16 +79,20 @@ describe('Dashboards list', () => {
|
||||
|
||||
it('should sort correctly in list mode', () => {
|
||||
cy.getBySel('sort-header').eq(1).click();
|
||||
cy.getBySel('loading-indicator').should('not.exist');
|
||||
cy.getBySel('table-row').first().contains('Supported Charts Dashboard');
|
||||
cy.getBySel('sort-header').eq(1).click();
|
||||
cy.getBySel('loading-indicator').should('not.exist');
|
||||
cy.getBySel('table-row').first().contains("World Bank's Data");
|
||||
cy.getBySel('sort-header').eq(1).click();
|
||||
});
|
||||
|
||||
it('should bulk select in list mode', () => {
|
||||
toggleBulkSelect();
|
||||
cy.get('#header-toggle-all').click();
|
||||
cy.get('[aria-label="checkbox-on"]').should('have.length', 6);
|
||||
cy.get('[aria-label="Select all"]').click();
|
||||
cy.get('.ant-checkbox-input')
|
||||
.should('be.checked')
|
||||
.should('have.length', 6);
|
||||
cy.getBySel('bulk-select-copy').contains('5 Selected');
|
||||
cy.getBySel('bulk-select-action')
|
||||
.should('have.length', 2)
|
||||
@@ -97,7 +101,7 @@ describe('Dashboards list', () => {
|
||||
expect($btns).to.contain('Export');
|
||||
});
|
||||
cy.getBySel('bulk-select-deselect-all').click();
|
||||
cy.get('[aria-label="checkbox-on"]').should('have.length', 0);
|
||||
cy.get('input[type="checkbox"]:checked').should('have.length', 0);
|
||||
cy.getBySel('bulk-select-copy').contains('0 Selected');
|
||||
cy.getBySel('bulk-select-action').should('not.exist');
|
||||
});
|
||||
@@ -195,6 +199,8 @@ describe('Dashboards list', () => {
|
||||
cy.get('[data-test="table-row"] input[type="checkbox"]').eq(1).click();
|
||||
cy.getBySel('bulk-select-action').eq(0).contains('Delete').click();
|
||||
confirmDelete(true);
|
||||
cy.getBySel('loading-indicator').should('exist');
|
||||
cy.getBySel('loading-indicator').should('not.exist');
|
||||
cy.getBySel('table-row')
|
||||
.eq(0)
|
||||
.should('not.contain', '3 - Sample dashboard');
|
||||
|
||||
@@ -30,36 +30,36 @@ import {
|
||||
const SAMPLE_DASHBOARDS_INDEXES = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
|
||||
function openDashboardsAddedTo() {
|
||||
cy.getBySel('actions-trigger').click();
|
||||
cy.get('.antd5-dropdown-menu-submenu-title')
|
||||
cy.getBySel('actions-trigger').should('be.visible').click();
|
||||
cy.get('.ant-dropdown-menu-submenu-title')
|
||||
.contains('On dashboards')
|
||||
.trigger('mouseover', { force: true });
|
||||
}
|
||||
|
||||
function closeDashboardsAddedTo() {
|
||||
cy.get('.antd5-dropdown-menu-submenu-title')
|
||||
cy.get('.ant-dropdown-menu-submenu-title')
|
||||
.contains('On dashboards')
|
||||
.trigger('mouseout', { force: true });
|
||||
cy.getBySel('actions-trigger').click();
|
||||
}
|
||||
|
||||
function verifyDashboardsSubmenuItem(dashboardName) {
|
||||
cy.get('.antd5-dropdown-menu-submenu-popup').contains(dashboardName);
|
||||
cy.get('.ant-dropdown-menu-submenu-popup').contains(dashboardName);
|
||||
closeDashboardsAddedTo();
|
||||
}
|
||||
|
||||
function verifyDashboardSearch() {
|
||||
openDashboardsAddedTo();
|
||||
cy.get('.antd5-dropdown-menu-submenu-popup').trigger('mouseover');
|
||||
cy.get('.antd5-dropdown-menu-submenu-popup')
|
||||
cy.get('.ant-dropdown-menu-submenu-popup').trigger('mouseover');
|
||||
cy.get('.ant-dropdown-menu-submenu-popup')
|
||||
.find('input[placeholder="Search"]')
|
||||
.type('1');
|
||||
cy.get('.antd5-dropdown-menu-submenu-popup').contains('1 - Sample dashboard');
|
||||
cy.get('.antd5-dropdown-menu-submenu-popup')
|
||||
cy.get('.ant-dropdown-menu-submenu-popup').contains('1 - Sample dashboard');
|
||||
cy.get('.ant-dropdown-menu-submenu-popup')
|
||||
.find('input[placeholder="Search"]')
|
||||
.type('Blahblah');
|
||||
cy.get('.antd5-dropdown-menu-submenu-popup').contains('No results found');
|
||||
cy.get('.antd5-dropdown-menu-submenu-popup')
|
||||
cy.get('.ant-dropdown-menu-submenu-popup').contains('No results found');
|
||||
cy.get('.ant-dropdown-menu-submenu-popup')
|
||||
.find('[aria-label="close-circle"]')
|
||||
.click();
|
||||
closeDashboardsAddedTo();
|
||||
@@ -68,8 +68,8 @@ function verifyDashboardSearch() {
|
||||
function verifyDashboardLink() {
|
||||
interceptDashboardGet();
|
||||
openDashboardsAddedTo();
|
||||
cy.get('.antd5-dropdown-menu-submenu-popup').trigger('mouseover');
|
||||
cy.get('.antd5-dropdown-menu-submenu-popup a')
|
||||
cy.get('.ant-dropdown-menu-submenu-popup').trigger('mouseover');
|
||||
cy.get('.ant-dropdown-menu-submenu-popup a')
|
||||
.first()
|
||||
.invoke('removeAttr', 'target')
|
||||
.click();
|
||||
@@ -80,8 +80,8 @@ function verifyMetabar(text) {
|
||||
cy.getBySel('metadata-bar').contains(text);
|
||||
}
|
||||
|
||||
function saveAndVerifyDashboard(number) {
|
||||
saveChartToDashboard(`${number} - Sample dashboard`);
|
||||
function saveAndVerifyDashboard(chartName, number) {
|
||||
saveChartToDashboard(chartName, `${number} - Sample dashboard`);
|
||||
verifyMetabar(
|
||||
number > 1 ? `Added to ${number} dashboards` : 'Added to 1 dashboard',
|
||||
);
|
||||
@@ -106,17 +106,17 @@ describe('Cross-referenced dashboards', () => {
|
||||
openDashboardsAddedTo();
|
||||
verifyDashboardsSubmenuItem('None');
|
||||
|
||||
saveAndVerifyDashboard('1');
|
||||
saveAndVerifyDashboard('2');
|
||||
saveAndVerifyDashboard('3');
|
||||
saveAndVerifyDashboard('4');
|
||||
saveAndVerifyDashboard('5');
|
||||
saveAndVerifyDashboard('6');
|
||||
saveAndVerifyDashboard('7');
|
||||
saveAndVerifyDashboard('8');
|
||||
saveAndVerifyDashboard('9');
|
||||
saveAndVerifyDashboard('10');
|
||||
saveAndVerifyDashboard('11');
|
||||
saveAndVerifyDashboard('1 - Sample chart', '1');
|
||||
saveAndVerifyDashboard('1 - Sample chart', '2');
|
||||
saveAndVerifyDashboard('1 - Sample chart', '3');
|
||||
saveAndVerifyDashboard('1 - Sample chart', '4');
|
||||
saveAndVerifyDashboard('1 - Sample chart', '5');
|
||||
saveAndVerifyDashboard('1 - Sample chart', '6');
|
||||
saveAndVerifyDashboard('1 - Sample chart', '7');
|
||||
saveAndVerifyDashboard('1 - Sample chart', '8');
|
||||
saveAndVerifyDashboard('1 - Sample chart', '9');
|
||||
saveAndVerifyDashboard('1 - Sample chart', '10');
|
||||
saveAndVerifyDashboard('1 - Sample chart', '11');
|
||||
|
||||
verifyDashboardSearch();
|
||||
verifyDashboardLink();
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
// ***********************************************
|
||||
// Tests for setting controls in the UI
|
||||
// ***********************************************
|
||||
import { interceptChart } from 'cypress/utils';
|
||||
import { interceptChart, setSelectSearchInput } from 'cypress/utils';
|
||||
|
||||
describe('Datasource control', () => {
|
||||
const newMetricName = `abc${Date.now()}`;
|
||||
@@ -40,44 +40,44 @@ describe('Datasource control', () => {
|
||||
// create new metric
|
||||
cy.get('[data-test="crud-add-table-item"]', { timeout: 10000 }).click();
|
||||
cy.wait(1000);
|
||||
cy.get(
|
||||
'[data-test="table-content-rows"] [data-test="editable-title-input"]',
|
||||
)
|
||||
cy.get('.ant-table-body [data-test="textarea-editable-title-input"]')
|
||||
.first()
|
||||
.click();
|
||||
|
||||
cy.get(
|
||||
'[data-test="table-content-rows"] [data-test="editable-title-input"]',
|
||||
)
|
||||
cy.get('.ant-table-body [data-test="textarea-editable-title-input"]')
|
||||
.first()
|
||||
.focus();
|
||||
cy.focused().clear({ force: true });
|
||||
cy.focused().type(`${newMetricName}{enter}`, { force: true });
|
||||
|
||||
cy.get('[data-test="datasource-modal-save"]').click();
|
||||
cy.get('.antd5-modal-confirm-btns button').contains('OK').click();
|
||||
cy.get('.ant-modal-confirm-btns button').contains('OK').click();
|
||||
// select new metric
|
||||
cy.get('[data-test=metrics]')
|
||||
.contains('Drop columns/metrics here or click')
|
||||
.click();
|
||||
|
||||
cy.get('input[aria-label="Select saved metrics"]').type(
|
||||
`${newMetricName}{enter}`,
|
||||
);
|
||||
cy.get('input[aria-label="Select saved metrics"]')
|
||||
.should('exist')
|
||||
.then($input => {
|
||||
setSelectSearchInput($input, newMetricName);
|
||||
});
|
||||
|
||||
// delete metric
|
||||
cy.get('[data-test="datasource-menu-trigger"]').click();
|
||||
cy.get('[data-test="edit-dataset"]').click();
|
||||
cy.get('.antd5-modal-content').within(() => {
|
||||
cy.get('.ant-modal-content').within(() => {
|
||||
cy.get('[data-test="collection-tab-Metrics"]')
|
||||
.contains('Metrics')
|
||||
.click();
|
||||
});
|
||||
cy.get(`input[value="${newMetricName}"]`)
|
||||
cy.get(`[data-test="textarea-editable-title-input"]`)
|
||||
.contains(newMetricName)
|
||||
.closest('tr')
|
||||
.find('[data-test="crud-delete-icon"]')
|
||||
.click();
|
||||
cy.get('[data-test="datasource-modal-save"]').click();
|
||||
cy.get('.antd5-modal-confirm-btns button').contains('OK').click();
|
||||
cy.get('.ant-modal-confirm-btns button').contains('OK').click();
|
||||
cy.get('[data-test="metrics"]').contains(newMetricName).should('not.exist');
|
||||
});
|
||||
});
|
||||
@@ -91,7 +91,7 @@ describe('Color scheme control', () => {
|
||||
});
|
||||
|
||||
it('should show color options with and without tooltips', () => {
|
||||
cy.get('#controlSections-tab-display').click();
|
||||
cy.get('#controlSections-tab-CUSTOMIZE').click();
|
||||
cy.get('.ant-select-selection-item .color-scheme-label').contains(
|
||||
'Superset Colors',
|
||||
);
|
||||
@@ -102,10 +102,19 @@ describe('Color scheme control', () => {
|
||||
cy.get('.color-scheme-tooltip').contains('Superset Colors');
|
||||
cy.get('.Control[data-test="color_scheme"]').scrollIntoView();
|
||||
cy.get('.Control[data-test="color_scheme"] input[type="search"]').focus();
|
||||
|
||||
cy.get('.color-scheme-label')
|
||||
.contains('Superset Colors')
|
||||
.trigger('mouseover');
|
||||
|
||||
cy.get('.color-scheme-label')
|
||||
.contains('Superset Colors')
|
||||
.trigger('mouseout');
|
||||
|
||||
cy.focused().type('lyftColors');
|
||||
cy.getBySel('lyftColors').should('exist');
|
||||
cy.getBySel('lyftColors').trigger('mouseover');
|
||||
cy.get('.color-scheme-tooltip').should('not.exist');
|
||||
cy.getBySel('lyftColors').trigger('mouseover', { force: true });
|
||||
cy.get('.color-scheme-tooltip').should('not.be.visible');
|
||||
});
|
||||
});
|
||||
describe('VizType control', () => {
|
||||
@@ -120,7 +129,7 @@ describe('VizType control', () => {
|
||||
|
||||
cy.contains('View all charts').click();
|
||||
|
||||
cy.get('.antd5-modal-content').within(() => {
|
||||
cy.get('.ant-modal-content').within(() => {
|
||||
cy.get('button').contains('KPI').click(); // change categories
|
||||
cy.get('[role="button"]').contains('Big Number').click();
|
||||
cy.get('button').contains('Select').click();
|
||||
|
||||
@@ -42,8 +42,8 @@ describe('Test explore links', () => {
|
||||
cy.wait('@chartData').then(() => {
|
||||
cy.get('code');
|
||||
});
|
||||
cy.get('.antd5-modal-content').within(() => {
|
||||
cy.get('button.antd5-modal-close').first().click({ force: true });
|
||||
cy.get('.ant-modal-content').within(() => {
|
||||
cy.get('button.ant-modal-close').first().click({ force: true });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -124,7 +124,7 @@ describe('Test explore links', () => {
|
||||
.find('input[aria-label="Select a dashboard"]')
|
||||
.type(`${dashboardTitle}`, { force: true });
|
||||
|
||||
cy.get(`.ant-select-item[label="${dashboardTitle}"]`).click({
|
||||
cy.get(`.ant-select-item[title="${dashboardTitle}"]`).click({
|
||||
force: true,
|
||||
});
|
||||
|
||||
@@ -157,7 +157,7 @@ describe('Test explore links', () => {
|
||||
.find('input[aria-label="Select a dashboard"]')
|
||||
.type(`${dashboardTitle}{enter}`, { force: true });
|
||||
|
||||
cy.get(`.ant-select-item[label="${dashboardTitle}"]`).click({
|
||||
cy.get(`.ant-select-item[title="${dashboardTitle}"]`).click({
|
||||
force: true,
|
||||
});
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ export function setFilter(filter: string, option: string) {
|
||||
cy.wait('@filtering');
|
||||
}
|
||||
|
||||
export function saveChartToDashboard(dashboardName: string) {
|
||||
export function saveChartToDashboard(chartName: string, dashboardName: string) {
|
||||
interceptDashboardGet();
|
||||
interceptUpdate();
|
||||
interceptExploreGet();
|
||||
@@ -75,23 +75,30 @@ export function saveChartToDashboard(dashboardName: string) {
|
||||
cy.getBySel('query-save-button')
|
||||
.should('be.enabled')
|
||||
.should('not.be.disabled')
|
||||
.click();
|
||||
cy.getBySelLike('chart-modal').should('be.visible');
|
||||
cy.get(
|
||||
'[data-test="save-chart-modal-select-dashboard-form"] [aria-label="Select a dashboard"]',
|
||||
)
|
||||
.first()
|
||||
.click();
|
||||
cy.get(
|
||||
'.ant-select-selection-search-input[aria-label="Select a dashboard"]',
|
||||
).type(dashboardName, { force: true });
|
||||
cy.get(`.ant-select-item-option[title="${dashboardName}"]`).click();
|
||||
cy.getBySel('btn-modal-save').click();
|
||||
.click({ force: true });
|
||||
|
||||
cy.wait('@update');
|
||||
cy.getBySel('save-modal-body')
|
||||
.should('be.visible')
|
||||
.then($modal => {
|
||||
cy.wait(500);
|
||||
cy.wrap($modal)
|
||||
.find(
|
||||
'.ant-select-selection-search-input[aria-label="Select a dashboard"]',
|
||||
)
|
||||
.type(dashboardName, { force: true });
|
||||
cy.wrap($modal)
|
||||
.find(`.ant-select-item-option[title="${dashboardName}"]`)
|
||||
.click();
|
||||
cy.getBySel('btn-modal-save').click();
|
||||
cy.wait('@update');
|
||||
});
|
||||
cy.getBySel('save-modal-body').should('not.exist');
|
||||
cy.getBySel('query-save-button').should('be.disabled');
|
||||
cy.wait('@get');
|
||||
cy.wait('@getExplore');
|
||||
cy.contains(`was added to dashboard [${dashboardName}]`);
|
||||
cy.contains(`Chart [${chartName}] has been overwritten`);
|
||||
cy.getBySel('query-save-button').should('be.enabled');
|
||||
}
|
||||
|
||||
export function visitSampleChartFromList(chartName: string) {
|
||||
|
||||
@@ -49,7 +49,7 @@ describe('Visualization > Box Plot', () => {
|
||||
it('should allow type to search color schemes', () => {
|
||||
verify(BOX_PLOT_FORM_DATA);
|
||||
|
||||
cy.get('#controlSections-tab-display').click();
|
||||
cy.get('#controlSections-tab-CUSTOMIZE').click();
|
||||
cy.get('.Control[data-test="color_scheme"]').scrollIntoView();
|
||||
cy.get('.Control[data-test="color_scheme"] input[type="search"]').focus();
|
||||
cy.focused().type('supersetColors{enter}');
|
||||
|
||||
@@ -89,7 +89,7 @@ describe('Visualization > Compare', () => {
|
||||
it('should allow type to search color schemes and apply the scheme', () => {
|
||||
verify(COMPARE_FORM_DATA);
|
||||
|
||||
cy.get('#controlSections-tab-display').click();
|
||||
cy.get('#controlSections-tab-CUSTOMIZE').click();
|
||||
cy.get('.Control[data-test="color_scheme"]').scrollIntoView();
|
||||
cy.get('.Control[data-test="color_scheme"] input[type="search"]').focus();
|
||||
cy.focused().type('supersetColors{enter}');
|
||||
|
||||
@@ -36,10 +36,10 @@ describe('Download Chart > Bar chart', () => {
|
||||
};
|
||||
|
||||
cy.visitChartByParams(formData);
|
||||
cy.get('.header-with-actions .antd5-dropdown-trigger').click();
|
||||
cy.get(':nth-child(3) > .antd5-dropdown-menu-submenu-title').click();
|
||||
cy.get('.header-with-actions .ant-dropdown-trigger').click();
|
||||
cy.get(':nth-child(3) > .ant-dropdown-menu-submenu-title').click();
|
||||
cy.get(
|
||||
'.antd5-dropdown-menu-submenu > .antd5-dropdown-menu li:nth-child(3)',
|
||||
'.ant-dropdown-menu-submenu > .ant-dropdown-menu li:nth-child(3)',
|
||||
).click();
|
||||
cy.verifyDownload('.jpg', {
|
||||
contains: true,
|
||||
|
||||
@@ -64,7 +64,7 @@ describe('Visualization > Gauge', () => {
|
||||
it('should allow type to search color schemes', () => {
|
||||
verify(GAUGE_FORM_DATA);
|
||||
|
||||
cy.get('#controlSections-tab-display').click();
|
||||
cy.get('#controlSections-tab-CUSTOMIZE').click();
|
||||
cy.get('.Control[data-test="color_scheme"]').scrollIntoView();
|
||||
cy.get('.Control[data-test="color_scheme"] input[type="search"]').focus();
|
||||
cy.focused().type('bnbColors{enter}');
|
||||
|
||||
@@ -80,7 +80,7 @@ describe('Visualization > Graph', () => {
|
||||
it('should allow type to search color schemes', () => {
|
||||
verify(GRAPH_FORM_DATA);
|
||||
|
||||
cy.get('#controlSections-tab-display').click();
|
||||
cy.get('#controlSections-tab-CUSTOMIZE').click();
|
||||
cy.get('.Control[data-test="color_scheme"]').scrollIntoView();
|
||||
cy.get('.Control[data-test="color_scheme"] input[type="search"]').focus();
|
||||
cy.focused().type('bnbColors{enter}');
|
||||
|
||||
@@ -71,7 +71,7 @@ describe('Visualization > Pie', () => {
|
||||
it('should allow type to search color schemes', () => {
|
||||
verify(PIE_FORM_DATA);
|
||||
|
||||
cy.get('#controlSections-tab-display').click();
|
||||
cy.get('#controlSections-tab-CUSTOMIZE').click();
|
||||
cy.get('.Control[data-test="color_scheme"]').scrollIntoView();
|
||||
cy.get('.Control[data-test="color_scheme"] input[type="search"]').focus();
|
||||
cy.focused().type('supersetColors{enter}');
|
||||
|
||||
@@ -86,7 +86,7 @@ describe('Visualization > Sunburst', () => {
|
||||
it('should allow type to search color schemes', () => {
|
||||
verify(SUNBURST_FORM_DATA);
|
||||
|
||||
cy.get('#controlSections-tab-display').click();
|
||||
cy.get('#controlSections-tab-CUSTOMIZE').click();
|
||||
cy.get('.Control[data-test="color_scheme"]').scrollIntoView();
|
||||
cy.get('.Control[data-test="color_scheme"] input[type="search"]').focus();
|
||||
cy.focused().type('supersetColors{enter}');
|
||||
|
||||
@@ -193,7 +193,9 @@ describe('Visualization > Table', () => {
|
||||
});
|
||||
|
||||
// should display in raw records mode
|
||||
cy.get('div[data-test="query_mode"] .btn.active').contains('Raw records');
|
||||
cy.get(
|
||||
'div[data-test="query_mode"] .ant-radio-button-wrapper-checked',
|
||||
).contains('Raw records');
|
||||
cy.get('div[data-test="all_columns"]').should('be.visible');
|
||||
cy.get('div[data-test="groupby"]').should('not.exist');
|
||||
|
||||
@@ -201,8 +203,12 @@ describe('Visualization > Table', () => {
|
||||
cy.get('[data-test="row-count-label"]').contains('100 rows');
|
||||
|
||||
// should allow switch back to aggregate mode
|
||||
cy.get('div[data-test="query_mode"] .btn').contains('Aggregate').click();
|
||||
cy.get('div[data-test="query_mode"] .btn.active').contains('Aggregate');
|
||||
cy.get('div[data-test="query_mode"] .ant-radio-button-wrapper')
|
||||
.contains('Aggregate')
|
||||
.click();
|
||||
cy.get(
|
||||
'div[data-test="query_mode"] .ant-radio-button-wrapper-checked',
|
||||
).contains('Aggregate');
|
||||
cy.get('div[data-test="all_columns"]').should('not.exist');
|
||||
cy.get('div[data-test="groupby"]').should('be.visible');
|
||||
});
|
||||
@@ -252,4 +258,217 @@ describe('Visualization > Table', () => {
|
||||
});
|
||||
cy.get('td').contains(/\d*%/);
|
||||
});
|
||||
|
||||
it('Test row limit with server pagination toggle', () => {
|
||||
const serverPaginationSelector =
|
||||
'[data-test="server_pagination-header"] div.pull-left [type="checkbox"]';
|
||||
cy.visitChartByParams({
|
||||
...VIZ_DEFAULTS,
|
||||
metrics: ['count'],
|
||||
row_limit: 100,
|
||||
});
|
||||
|
||||
// Enable server pagination
|
||||
cy.get(serverPaginationSelector).click();
|
||||
|
||||
// Click row limit control and select high value (200k)
|
||||
cy.get('div[aria-label="Row limit"]').click();
|
||||
|
||||
// Type 200000 and press enter to select the option
|
||||
cy.get('div[aria-label="Row limit"]')
|
||||
.find('.ant-select-selection-search-input:visible')
|
||||
.type('200000{enter}');
|
||||
|
||||
// Verify that there is no error tooltip when server pagination is enabled
|
||||
cy.get('[data-test="error-tooltip"]').should('not.exist');
|
||||
|
||||
// Disable server pagination
|
||||
cy.get(serverPaginationSelector).click();
|
||||
|
||||
// Verify error tooltip appears
|
||||
cy.get('[data-test="error-tooltip"]').should('be.visible');
|
||||
|
||||
// Trigger mouseover and verify tooltip text
|
||||
cy.get('[data-test="error-tooltip"]').trigger('mouseover');
|
||||
|
||||
// Verify tooltip content
|
||||
cy.get('.ant-tooltip-inner').should('be.visible');
|
||||
cy.get('.ant-tooltip-inner').should(
|
||||
'contain',
|
||||
'Server pagination needs to be enabled for values over',
|
||||
);
|
||||
|
||||
// Hide the tooltip by adding display:none style
|
||||
cy.get('.ant-tooltip').invoke('attr', 'style', 'display: none');
|
||||
|
||||
// Enable server pagination again
|
||||
cy.get(serverPaginationSelector).click();
|
||||
|
||||
cy.get('[data-test="error-tooltip"]').should('not.exist');
|
||||
|
||||
cy.get('div[aria-label="Row limit"]').click();
|
||||
|
||||
// Type 1000000
|
||||
cy.get('div[aria-label="Row limit"]')
|
||||
.find('.ant-select-selection-search-input:visible')
|
||||
.type('1000000');
|
||||
|
||||
// Wait for 1 second
|
||||
cy.wait(1000);
|
||||
|
||||
// Press enter
|
||||
cy.get('div[aria-label="Row limit"]')
|
||||
.find('.ant-select-selection-search-input:visible')
|
||||
.type('{enter}');
|
||||
|
||||
// Wait for error tooltip to appear and verify its content
|
||||
cy.get('[data-test="error-tooltip"]')
|
||||
.should('be.visible')
|
||||
.trigger('mouseover');
|
||||
|
||||
// Wait for tooltip content and verify
|
||||
cy.get('.ant-tooltip-inner').should('exist');
|
||||
cy.get('.ant-tooltip-inner').should('be.visible');
|
||||
|
||||
// Verify tooltip content separately
|
||||
cy.get('.ant-tooltip-inner').should('contain', 'Value cannot exceed');
|
||||
});
|
||||
|
||||
it('Test sorting with server pagination enabled', () => {
|
||||
cy.visitChartByParams({
|
||||
...VIZ_DEFAULTS,
|
||||
metrics: ['count'],
|
||||
groupby: ['name'],
|
||||
row_limit: 100000,
|
||||
server_pagination: true, // Enable server pagination
|
||||
});
|
||||
|
||||
// Wait for the initial data load
|
||||
cy.wait('@chartData');
|
||||
|
||||
// Get the first column header (name)
|
||||
cy.get('.chart-container th').contains('name').as('nameHeader');
|
||||
|
||||
// Click to sort ascending
|
||||
cy.get('@nameHeader').click();
|
||||
cy.wait('@chartData');
|
||||
|
||||
// Verify first row starts with 'A'
|
||||
cy.get('.chart-container td:first').invoke('text').should('match', /^[Aa]/);
|
||||
|
||||
// Click again to sort descending
|
||||
cy.get('@nameHeader').click();
|
||||
cy.wait('@chartData');
|
||||
|
||||
// Verify first row starts with 'Z'
|
||||
cy.get('.chart-container td:first').invoke('text').should('match', /^[Zz]/);
|
||||
|
||||
// Test numeric sorting
|
||||
cy.get('.chart-container th').contains('COUNT').as('countHeader');
|
||||
|
||||
// Click to sort ascending by count
|
||||
cy.get('@countHeader').click();
|
||||
cy.wait('@chartData');
|
||||
|
||||
// Get first two count values and verify ascending order
|
||||
cy.get('.chart-container td:nth-child(2)').then($cells => {
|
||||
const first = parseFloat($cells[0].textContent || '0');
|
||||
const second = parseFloat($cells[1].textContent || '0');
|
||||
expect(first).to.be.at.most(second);
|
||||
});
|
||||
|
||||
// Click again to sort descending
|
||||
cy.get('@countHeader').click();
|
||||
cy.wait('@chartData');
|
||||
|
||||
// Get first two count values and verify descending order
|
||||
cy.get('.chart-container td:nth-child(2)').then($cells => {
|
||||
const first = parseFloat($cells[0].textContent || '0');
|
||||
const second = parseFloat($cells[1].textContent || '0');
|
||||
expect(first).to.be.at.least(second);
|
||||
});
|
||||
});
|
||||
|
||||
it('Test search with server pagination enabled', () => {
|
||||
cy.visitChartByParams({
|
||||
...VIZ_DEFAULTS,
|
||||
metrics: ['count'],
|
||||
groupby: ['name', 'state'],
|
||||
row_limit: 100000,
|
||||
server_pagination: true,
|
||||
include_search: true,
|
||||
});
|
||||
|
||||
cy.wait('@chartData');
|
||||
|
||||
const searchInputSelector = '.dt-global-filter input';
|
||||
|
||||
// Basic search test
|
||||
cy.get(searchInputSelector).should('be.visible');
|
||||
|
||||
cy.get(searchInputSelector).type('John');
|
||||
|
||||
cy.wait('@chartData');
|
||||
|
||||
cy.get('.chart-container tbody tr').each($row => {
|
||||
cy.wrap($row).contains(/John/i);
|
||||
});
|
||||
|
||||
// Clear and test case-insensitive search
|
||||
cy.get(searchInputSelector).clear();
|
||||
|
||||
cy.wait('@chartData');
|
||||
|
||||
cy.get(searchInputSelector).type('mary');
|
||||
|
||||
cy.wait('@chartData');
|
||||
|
||||
cy.get('.chart-container tbody tr').each($row => {
|
||||
cy.wrap($row).contains(/Mary/i);
|
||||
});
|
||||
|
||||
// Test special characters
|
||||
cy.get(searchInputSelector).clear();
|
||||
|
||||
cy.get(searchInputSelector).type('Nicole');
|
||||
|
||||
cy.wait('@chartData');
|
||||
|
||||
cy.get('.chart-container tbody tr').each($row => {
|
||||
cy.wrap($row).contains(/Nicole/i);
|
||||
});
|
||||
|
||||
// Test no results
|
||||
cy.get(searchInputSelector).clear();
|
||||
|
||||
cy.get(searchInputSelector).type('XYZ123');
|
||||
|
||||
cy.wait('@chartData');
|
||||
|
||||
cy.get('.chart-container').contains('No records found');
|
||||
|
||||
// Test column-specific search
|
||||
cy.get('.search-select').should('be.visible');
|
||||
|
||||
cy.get('.search-select').click();
|
||||
|
||||
cy.get('.ant-select-dropdown').should('be.visible');
|
||||
|
||||
cy.get('.ant-select-item-option').contains('state').should('be.visible');
|
||||
|
||||
cy.get('.ant-select-item-option').contains('state').click();
|
||||
|
||||
cy.get(searchInputSelector).clear();
|
||||
|
||||
cy.get(searchInputSelector).type('CA');
|
||||
|
||||
cy.wait('@chartData');
|
||||
cy.wait(1000);
|
||||
|
||||
cy.get('td[aria-labelledby="header-state"]').should('be.visible');
|
||||
|
||||
cy.get('td[aria-labelledby="header-state"]')
|
||||
.first()
|
||||
.should('contain', 'CA');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -22,7 +22,7 @@ describe('SqlLab query tabs', () => {
|
||||
});
|
||||
|
||||
const tablistSelector = '[data-test="sql-editor-tabs"] > [role="tablist"]';
|
||||
const tabSelector = `${tablistSelector} [role="tab"]`;
|
||||
const tabSelector = `${tablistSelector} [role="tab"]:not([type="button"])`;
|
||||
|
||||
it('allows you to create and close a tab', () => {
|
||||
cy.get(tabSelector).then(tabs => {
|
||||
@@ -80,9 +80,9 @@ describe('SqlLab query tabs', () => {
|
||||
// configure some editor settings
|
||||
cy.get(editorInput).type('some random query string', { force: true });
|
||||
cy.get(queryLimitSelector).parent().click({ force: true });
|
||||
cy.get('.antd5-dropdown-menu')
|
||||
cy.get('.ant-dropdown-menu')
|
||||
.last()
|
||||
.find('.antd5-dropdown-menu-item')
|
||||
.find('.ant-dropdown-menu-item')
|
||||
.first()
|
||||
.click({ force: true });
|
||||
|
||||
|
||||
@@ -25,16 +25,16 @@ export function dataTestChartName(chartName: string): string {
|
||||
|
||||
export const pageHeader = {
|
||||
logo: '.navbar-brand > img',
|
||||
headerNavigationItem: '.antd5-menu-submenu-title',
|
||||
headerNavigationItem: '.ant-menu-submenu-title',
|
||||
headerNavigationDropdown: "[aria-label='triangle-down']",
|
||||
headerNavigationItemMenu: '.antd5-menu-item-group-list',
|
||||
plusIcon: ':nth-child(2) > .antd5-menu-submenu-title',
|
||||
headerNavigationItemMenu: '.ant-menu-item-group-list',
|
||||
plusIcon: ':nth-child(2) > .ant-menu-submenu-title',
|
||||
plusIconMenuOptions: {
|
||||
sqlQueryOption: dataTestLocator('menu-item-SQL query'),
|
||||
chartOption: dataTestLocator('menu-item-Chart'),
|
||||
dashboardOption: dataTestLocator('menu-item-Dashboard'),
|
||||
},
|
||||
plusMenu: '.antd5-menu-submenu-popup',
|
||||
plusMenu: '.ant-menu-submenu-popup',
|
||||
barButtons: '[role="presentation"]',
|
||||
sqlLabMenu: '[id="item_3$Menu"]',
|
||||
dataMenu: '[id="item_4$Menu"]',
|
||||
@@ -48,12 +48,12 @@ export const profile = {
|
||||
favoritesSpace: '#rc-tabs-0-panel-2',
|
||||
};
|
||||
export const securityAccess = {
|
||||
rolesBubble: '.antd5-badge-count',
|
||||
rolesBubble: '.ant-badge-count',
|
||||
};
|
||||
export const homePage = {
|
||||
homeSection: {
|
||||
sectionArea: '.ant-collapse-content-box',
|
||||
sectionElement: '.antd5-card-meta-title',
|
||||
sectionElement: '.ant-card-meta-title',
|
||||
},
|
||||
sections: {
|
||||
expandedSection: '.ant-collapse-item-active',
|
||||
@@ -94,19 +94,19 @@ export const databasesPage = {
|
||||
dbDropdown: '[class="ant-select-selection-search-input"]',
|
||||
dbDropdownMenu: '.rc-virtual-list-holder-inner',
|
||||
dbDropdownMenuItem: '[class="ant-select-item-option-content"]',
|
||||
infoAlert: '.antd5-alert',
|
||||
infoAlert: '.ant-alert',
|
||||
serviceAccountInput: '[name="credentials_info"]',
|
||||
connectionStep: {
|
||||
modal: '.antd5-modal-content',
|
||||
modalBody: '.antd5-modal-body',
|
||||
modal: '.ant-modal-content',
|
||||
modalBody: '.ant-modal-body',
|
||||
stepTitle: '.css-7x6kk > h4',
|
||||
helperBottom: '.helper-bottom',
|
||||
postgresDatabase: '[name="database"]',
|
||||
dbInput: '[name="database_name"]',
|
||||
alertMessage: '.antd5-alert-message',
|
||||
alertMessage: '.ant-alert-message',
|
||||
errorField: '[role="alert"]',
|
||||
uploadJson: '[title="Upload JSON file"]',
|
||||
chooseFile: '[class="antd5-btn input-upload-btn"]',
|
||||
chooseFile: '[class="ant-btn input-upload-btn"]',
|
||||
additionalParameters: '[name="query_input"]',
|
||||
sqlAlchemyUriInput: dataTestLocator('sqlalchemy-uri-input'),
|
||||
advancedTab: '#rc-tabs-0-tab-2',
|
||||
@@ -140,7 +140,7 @@ export const sqlLabView = {
|
||||
tabsNavList: "[class='ant-tabs-nav-list']",
|
||||
tab: "[class='ant-tabs-tab-btn']",
|
||||
addTabButton: dataTestLocator('add-tab-icon'),
|
||||
tooltip: '.antd5-tooltip-content',
|
||||
tooltip: '.ant-tooltip-content',
|
||||
tabName: '.css-1suejie',
|
||||
schemaInput: '[data-test=DatabaseSelector] > :nth-child(2)',
|
||||
loadingIndicator: '.Select__loading-indicator',
|
||||
@@ -148,9 +148,9 @@ export const sqlLabView = {
|
||||
examplesMenuItem: '[title="examples"]',
|
||||
tableInput: ':nth-child(4) > .select > :nth-child(1)',
|
||||
sqlEditor: '#brace-editor textarea',
|
||||
saveAsButton: '.SaveQuery > .antd5-btn',
|
||||
saveAsButton: '.SaveQuery > .ant-btn',
|
||||
saveAsModal: {
|
||||
footer: '.antd5-modal-footer',
|
||||
footer: '.ant-modal-footer',
|
||||
queryNameInput: 'input[class^="ant-input"]',
|
||||
},
|
||||
sqlToolbar: {
|
||||
@@ -158,15 +158,15 @@ export const sqlLabView = {
|
||||
runButton: '.css-d3dxop',
|
||||
},
|
||||
rowsLimit: {
|
||||
dropdown: '.antd5-dropdown-menu',
|
||||
limitButton: '.antd5-dropdown-menu-item',
|
||||
dropdown: '.ant-dropdown-menu',
|
||||
limitButton: '.ant-dropdown-menu-item',
|
||||
limitButtonText: '.css-151uxnz',
|
||||
limitTextWithValue: '[class="antd5-dropdown-trigger"]',
|
||||
limitTextWithValue: '[class="ant-dropdown-trigger"]',
|
||||
},
|
||||
renderedTableHeader: '.ReactVirtualized__Table__headerRow',
|
||||
renderedTableRow: '.ReactVirtualized__Table__row',
|
||||
errorBody: '.error-body',
|
||||
alertMessage: '.antd5-alert-message',
|
||||
alertMessage: '.ant-alert-message',
|
||||
historyTable: {
|
||||
header: '[role=columnheader]',
|
||||
table: '.QueryTable',
|
||||
@@ -195,16 +195,16 @@ export const savedQuery = {
|
||||
export const annotationLayersView = {
|
||||
emptyDescription: {
|
||||
description: '.ant-empty-description',
|
||||
addAnnotationLayerButton: '.ant-empty-footer > .antd5-btn',
|
||||
addAnnotationLayerButton: '.ant-empty-footer > .ant-btn',
|
||||
},
|
||||
modal: {
|
||||
content: {
|
||||
content: '.antd5-modal-body',
|
||||
title: '.antd5-modal-body > :nth-child(2) > input',
|
||||
content: '.ant-modal-body',
|
||||
title: '.ant-modal-body > :nth-child(2) > input',
|
||||
description: "[name='descr']",
|
||||
},
|
||||
footer: {
|
||||
footer: '.antd5-modal-footer',
|
||||
footer: '.ant-modal-footer',
|
||||
addButton: dataTestLocator('modal-confirm-button'),
|
||||
cancelButton: dataTestLocator('modal-cancel-button'),
|
||||
},
|
||||
@@ -216,7 +216,7 @@ export const datasetsList = {
|
||||
newDatasetModal: {
|
||||
inputField: '[class="section"]',
|
||||
addButton: dataTestLocator('modal-confirm-button'),
|
||||
body: '.antd5-modal-body',
|
||||
body: '.ant-modal-body',
|
||||
},
|
||||
table: {
|
||||
tableRow: {
|
||||
@@ -261,7 +261,7 @@ export const datasetsList = {
|
||||
},
|
||||
},
|
||||
deleteDatasetModal: {
|
||||
modal: '.antd5-modal-content',
|
||||
modal: '.ant-modal-content',
|
||||
deleteInput: dataTestLocator('delete-modal-input'),
|
||||
deleteButton: dataTestLocator('modal-confirm-button'),
|
||||
text: '.css-kxmt87',
|
||||
@@ -275,8 +275,8 @@ export const chartListView = {
|
||||
bulkSelect: dataTestLocator('bulk-select'),
|
||||
},
|
||||
header: {
|
||||
cardView: '[aria-label="appstore"]',
|
||||
listView: '[aria-label="unordered-list"]',
|
||||
cardView: '[aria-label="card-view"]',
|
||||
listView: '[aria-label="list-view"]',
|
||||
sort: '[class="ant-select-selection-search-input"][aria-label="Sort"]',
|
||||
sortRecentlyModifiedMenuOption: '[label="Recently modified"]',
|
||||
sortAlphabeticalMenuOption: '[label="Alphabetical"]',
|
||||
@@ -284,7 +284,7 @@ export const chartListView = {
|
||||
},
|
||||
card: {
|
||||
card: dataTestLocator('styled-card'),
|
||||
cardCover: '[class="antd5-card-cover"]',
|
||||
cardCover: '[class="ant-card-cover"]',
|
||||
cardImage: '[class="gradient-container"]',
|
||||
starIcon: dataTestLocator('fave-unfave-icon'),
|
||||
},
|
||||
@@ -294,8 +294,8 @@ export const chartListView = {
|
||||
},
|
||||
table: {
|
||||
bulkSelect: {
|
||||
checkboxOff: '[aria-label="checkbox-off"]',
|
||||
checkboxOn: '[aria-label="checkbox-on"]',
|
||||
checkboxOff: 'input[type="checkbox"]:checked',
|
||||
checkboxOn: 'input[type="checkbox"]:not(:checked)',
|
||||
action: dataTestLocator('bulk-select-action'),
|
||||
},
|
||||
tableList: dataTestLocator('listview-table'),
|
||||
@@ -316,14 +316,14 @@ export const chartListView = {
|
||||
};
|
||||
export const nativeFilters = {
|
||||
modal: {
|
||||
container: '.antd5-modal',
|
||||
footer: '.antd5-modal-footer',
|
||||
container: '.ant-modal',
|
||||
footer: '.ant-modal-footer',
|
||||
saveButton: dataTestLocator('native-filter-modal-save-button'),
|
||||
cancelButton: dataTestLocator('native-filter-modal-cancel-button'),
|
||||
confirmCancelButton: dataTestLocator(
|
||||
'native-filter-modal-confirm-cancel-button',
|
||||
),
|
||||
alertXUnsavedFilters: '.antd5-alert-message',
|
||||
alertXUnsavedFilters: '.ant-alert-message',
|
||||
tabsList: {
|
||||
filterItemsContainer: dataTestLocator('filter-title-container'),
|
||||
tabsContainer: '[class="ant-tabs-nav-list"]',
|
||||
@@ -398,7 +398,7 @@ export const dashboardListView = {
|
||||
},
|
||||
card: {
|
||||
card: dataTestLocator('styled-card'),
|
||||
cardCover: '[class="antd5-card-cover"]',
|
||||
cardCover: '[class="ant-card-cover"]',
|
||||
cardImage: '[class="gradient-container"]',
|
||||
selectedStarIcon: "[aria-label='star']",
|
||||
unselectedStarIcon: "[aria-label='star']",
|
||||
@@ -432,7 +432,7 @@ export const dashboardListView = {
|
||||
newDashboardButton: '.css-yff34v',
|
||||
},
|
||||
importModal: {
|
||||
selectFileButton: '.ant-upload > .antd5-btn > span',
|
||||
selectFileButton: '.ant-upload > .ant-btn > span',
|
||||
importButton: dataTestLocator('modal-confirm-button'),
|
||||
},
|
||||
header: {
|
||||
@@ -474,15 +474,15 @@ export const exploreView = {
|
||||
},
|
||||
chartAreaItem: '.nv-legend-text',
|
||||
viewQueryModal: {
|
||||
container: '.antd5-modal-content',
|
||||
closeButton: 'button.antd5-modal-close',
|
||||
container: '.ant-modal-content',
|
||||
closeButton: 'button.ant-modal-close',
|
||||
},
|
||||
embedCodeModal: {
|
||||
container: dataTestLocator('embed-code-popover'),
|
||||
textfield: dataTestLocator('embed-code-textarea'),
|
||||
},
|
||||
saveModal: {
|
||||
modal: '.antd5-modal-content',
|
||||
modal: '.ant-modal-content',
|
||||
chartNameInput: dataTestLocator('new-chart-name'),
|
||||
dashboardNameInput: '.ant-select-selection-search-input',
|
||||
addToDashboardInput: dataTestLocator(
|
||||
@@ -553,7 +553,7 @@ export const exploreView = {
|
||||
timeSection: {
|
||||
timeRangeFilter: dataTestLocator('time-range-trigger'),
|
||||
timeRangeFilterModal: {
|
||||
container: '.antd5-popover-content',
|
||||
container: '.ant-popover-content',
|
||||
footer: '.footer',
|
||||
cancelButton: dataTestLocator('cancel-button'),
|
||||
configureLastTimeRange: {
|
||||
@@ -578,15 +578,15 @@ export const exploreView = {
|
||||
},
|
||||
},
|
||||
editDatasetModal: {
|
||||
container: '.antd5-modal-content',
|
||||
container: '.ant-modal-content',
|
||||
datasetTabsContainer: dataTestLocator('edit-dataset-tabs'),
|
||||
saveButton: dataTestLocator('datasource-modal-save'),
|
||||
metricsTab: {
|
||||
addItem: dataTestLocator('crud-add-table-item'),
|
||||
rowsContainer: dataTestLocator('table-content-rows'),
|
||||
rowsContainer: '.ant-table-body',
|
||||
},
|
||||
confirmModal: {
|
||||
okButton: '.antd5-modal-confirm-btns .antd5-btn-primary',
|
||||
okButton: '.ant-modal-confirm-btns .ant-btn-primary',
|
||||
},
|
||||
},
|
||||
visualizationTypeModal: {
|
||||
@@ -617,12 +617,12 @@ export const dashboardView = {
|
||||
closeButton: dataTestLocator('close-button'),
|
||||
},
|
||||
saveModal: {
|
||||
modal: '.antd5-modal-content',
|
||||
modal: '.ant-modal-content',
|
||||
dashboardNameInput: '.ant-input',
|
||||
saveButton: dataTestLocator('modal-save-dashboard-button'),
|
||||
},
|
||||
dashboardProperties: {
|
||||
modal: '.antd5-modal-content',
|
||||
modal: '.ant-modal-content',
|
||||
dashboardTitleInput: dataTestLocator('dashboard-title-input'),
|
||||
modalButton: '[type="button"]',
|
||||
},
|
||||
@@ -631,7 +631,7 @@ export const dashboardView = {
|
||||
refreshChart: dataTestLocator('refresh-chart-menu-item'),
|
||||
},
|
||||
threeDotsMenuIcon:
|
||||
'.header-with-actions .right-button-panel .antd5-dropdown-trigger',
|
||||
'.header-with-actions .right-button-panel .ant-dropdown-trigger',
|
||||
threeDotsMenuDropdown: dataTestLocator('header-actions-menu'),
|
||||
refreshDashboard: dataTestLocator('refresh-dashboard-menu-item'),
|
||||
saveAsMenuOption: dataTestLocator('save-as-menu-item'),
|
||||
|
||||
@@ -69,7 +69,21 @@ Cypress.Commands.add('loadDashboardFixtures', () =>
|
||||
}),
|
||||
);
|
||||
|
||||
const PATHS_TO_SKIP_LOGIN = ['login', 'register'];
|
||||
|
||||
const skipLogin = () => {
|
||||
for (const path of PATHS_TO_SKIP_LOGIN) {
|
||||
if (Cypress.currentTest.title.toLowerCase().includes(path)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
before(() => {
|
||||
if (skipLogin()) {
|
||||
return;
|
||||
}
|
||||
cy.login();
|
||||
Cypress.Cookies.defaults({ preserve: 'session' });
|
||||
cy.loadChartFixtures();
|
||||
@@ -77,6 +91,9 @@ before(() => {
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
if (skipLogin()) {
|
||||
return;
|
||||
}
|
||||
cy.cleanDashboards();
|
||||
cy.cleanCharts();
|
||||
});
|
||||
|
||||
@@ -143,3 +143,29 @@ export function resize(selector: string) {
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export const setSelectSearchInput = (
|
||||
$input: any,
|
||||
value: string,
|
||||
async = false,
|
||||
) => {
|
||||
// Ant Design 5 Select crashes Chromium with type/click events when showSearch is true.
|
||||
// This copies the value directly to the input element as a workaround.
|
||||
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
|
||||
window.HTMLInputElement.prototype,
|
||||
'value',
|
||||
)?.set;
|
||||
nativeInputValueSetter?.call($input[0], value);
|
||||
|
||||
// Trigger the input and change events
|
||||
if (async) {
|
||||
$input[0].dispatchEvent(new Event('mousedown', { bubbles: true }));
|
||||
}
|
||||
|
||||
$input[0].dispatchEvent(new Event('input', { bubbles: true }));
|
||||
$input[0].dispatchEvent(new Event('change', { bubbles: true }));
|
||||
|
||||
cy.get('.ant-select-item-option-content').should('exist').first().click({
|
||||
force: true,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -28,3 +28,5 @@ export const DATABASE_LIST = '/databaseview/list';
|
||||
export const DATASET_LIST_PATH = 'tablemodelview/list';
|
||||
export const ALERT_LIST = '/alert/list/';
|
||||
export const REPORT_LIST = '/report/list/';
|
||||
export const LOGIN = '/login/';
|
||||
export const REGISTER = '/register/';
|
||||
|
||||
@@ -16,10 +16,8 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
// timezone for unit tests
|
||||
process.env.TZ = 'America/New_York';
|
||||
|
||||
module.exports = {
|
||||
testRegex:
|
||||
'\\/superset-frontend\\/(spec|src|plugins|packages|tools)\\/.*(_spec|\\.test)\\.[jt]sx?$',
|
||||
@@ -30,7 +28,9 @@ module.exports = {
|
||||
'^src/(.*)$': '<rootDir>/src/$1',
|
||||
'^spec/(.*)$': '<rootDir>/spec/$1',
|
||||
// mapping plugins of superset-ui to source code
|
||||
'@superset-ui/(.*)$': '<rootDir>/node_modules/@superset-ui/$1/src',
|
||||
'^@superset-ui/([^/]+)/(.*)$':
|
||||
'<rootDir>/node_modules/@superset-ui/$1/src/$2',
|
||||
'^@superset-ui/([^/]+)$': '<rootDir>/node_modules/@superset-ui/$1/src',
|
||||
},
|
||||
testEnvironment: 'jsdom',
|
||||
modulePathIgnorePatterns: ['<rootDir>/packages/generator-superset'],
|
||||
@@ -55,7 +55,7 @@ module.exports = {
|
||||
],
|
||||
coverageReporters: ['lcov', 'json-summary', 'html', 'text'],
|
||||
transformIgnorePatterns: [
|
||||
'node_modules/(?!d3-(interpolate|color|time)|remark-gfm|markdown-table|micromark-*.|decode-named-character-reference|character-entities|mdast-util-*.|unist-util-*.|ccount|escape-string-regexp|nanoid|@rjsf/*.|sinon|echarts|zrender|fetch-mock|pretty-ms|parse-ms|ol|@babel/runtime|@emotion|cheerio|cheerio/lib|parse5|dom-serializer|entities|htmlparser2|rehype-sanitize|hast-util-sanitize|unified|unist-.*|hast-.*|rehype-.*|remark-.*|mdast-.*|micromark-.*|parse-entities|property-information|space-separated-tokens|comma-separated-tokens|bail|devlop|zwitch|longest-streak|jest-enzyme)',
|
||||
'node_modules/(?!d3-(interpolate|color|time)|remark-gfm|markdown-table|micromark-*.|decode-named-character-reference|character-entities|mdast-util-*.|unist-util-*.|ccount|escape-string-regexp|nanoid|@rjsf/*.|sinon|echarts|zrender|fetch-mock|pretty-ms|parse-ms|ol|@babel/runtime|@emotion|cheerio|cheerio/lib|parse5|dom-serializer|entities|htmlparser2|rehype-sanitize|hast-util-sanitize|unified|unist-.*|hast-.*|rehype-.*|remark-.*|mdast-.*|micromark-.*|parse-entities|property-information|space-separated-tokens|comma-separated-tokens|bail|devlop|zwitch|longest-streak|jest-enzyme|geostyler|geostyler-.*)',
|
||||
],
|
||||
preset: 'ts-jest',
|
||||
transform: {
|
||||
|
||||
12200
superset-frontend/package-lock.json
generated
12200
superset-frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -37,7 +37,7 @@
|
||||
"src/setup/*"
|
||||
],
|
||||
"scripts": {
|
||||
"_prettier": "prettier './({src,spec,cypress-base,plugins,packages,.storybook}/**/*{.js,.jsx,.ts,.tsx,.css,.less,.scss,.sass}|package.json)'",
|
||||
"_prettier": "prettier './({src,spec,cypress-base,plugins,packages,.storybook}/**/*{.js,.jsx,.ts,.tsx,.css,.scss,.sass}|package.json)'",
|
||||
"build": "cross-env NODE_OPTIONS=--max_old_space_size=8192 NODE_ENV=production BABEL_ENV=\"${BABEL_ENV:=production}\" webpack --color --mode production",
|
||||
"build-dev": "cross-env NODE_OPTIONS=--max_old_space_size=8192 NODE_ENV=development webpack --mode=development --color",
|
||||
"build-instrumented": "cross-env NODE_ENV=production BABEL_ENV=instrumented webpack --mode=production --color",
|
||||
@@ -70,6 +70,7 @@
|
||||
"storybook": "cross-env NODE_ENV=development BABEL_ENV=development storybook dev -p 6006",
|
||||
"tdd": "cross-env NODE_ENV=test NODE_OPTIONS=\"--max-old-space-size=8192\" jest --watch",
|
||||
"test": "cross-env NODE_ENV=test NODE_OPTIONS=\"--max-old-space-size=8192\" jest --max-workers=80% --silent",
|
||||
"test-loud": "cross-env NODE_ENV=test NODE_OPTIONS=\"--max-old-space-size=8192\" jest --max-workers=80%",
|
||||
"type": "tsc --noEmit",
|
||||
"update-maps": "jupyter nbconvert --to notebook --execute --inplace 'plugins/legacy-plugin-chart-country-map/scripts/Country Map GeoJSON Generator.ipynb' -Xfrozen_modules=off",
|
||||
"validate-release": "../RELEASING/validate_this_release.sh"
|
||||
@@ -81,12 +82,9 @@
|
||||
"last 3 edge versions"
|
||||
],
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^5.2.6",
|
||||
"@emotion/cache": "^11.4.0",
|
||||
"@emotion/react": "^11.13.3",
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@emotion/styled": "^11.3.0",
|
||||
"@fontsource/fira-code": "^5.0.18",
|
||||
"@fontsource/inter": "^5.0.20",
|
||||
"@reduxjs/toolkit": "^1.9.3",
|
||||
"@rjsf/core": "^5.21.1",
|
||||
"@rjsf/utils": "^5.24.3",
|
||||
@@ -115,6 +113,7 @@
|
||||
"@superset-ui/switchboard": "file:./packages/superset-ui-switchboard",
|
||||
"@types/d3-format": "^3.0.1",
|
||||
"@types/d3-time-format": "^4.0.3",
|
||||
"@types/react-google-recaptcha": "^2.1.9",
|
||||
"@visx/axis": "^3.8.0",
|
||||
"@visx/grid": "^3.5.0",
|
||||
"@visx/responsive": "^3.0.0",
|
||||
@@ -122,16 +121,11 @@
|
||||
"@visx/tooltip": "^3.0.0",
|
||||
"@visx/xychart": "^3.5.1",
|
||||
"abortcontroller-polyfill": "^1.7.8",
|
||||
"ace-builds": "^1.36.3",
|
||||
"ag-grid-community": "33.1.1",
|
||||
"ag-grid-react": "33.1.1",
|
||||
"antd": "4.10.3",
|
||||
"antd-v5": "npm:antd@^5.18.0",
|
||||
"bootstrap": "^3.4.1",
|
||||
"brace": "^0.11.1",
|
||||
"antd": "^5.24.6",
|
||||
"chrono-node": "^2.7.8",
|
||||
"classnames": "^2.2.5",
|
||||
"core-js": "^3.38.1",
|
||||
"d3-color": "^3.1.0",
|
||||
"d3-scale": "^2.1.2",
|
||||
"dayjs": "^1.11.13",
|
||||
@@ -144,12 +138,14 @@
|
||||
"fs-extra": "^11.2.0",
|
||||
"fuse.js": "^7.0.0",
|
||||
"geolib": "^2.0.24",
|
||||
"geostyler": "^12.0.2",
|
||||
"geostyler": "^14.1.3",
|
||||
"geostyler-data": "^1.0.0",
|
||||
"geostyler-openlayers-parser": "^4.3.0",
|
||||
"geostyler-style": "^7.5.0",
|
||||
"geostyler-qgis-parser": "2.0.1",
|
||||
"geostyler-style": "7.5.0",
|
||||
"geostyler-wfs-parser": "^2.0.3",
|
||||
"googleapis": "^130.0.0",
|
||||
"html-webpack-plugin": "^5.6.3",
|
||||
"immer": "^10.1.1",
|
||||
"interweave": "^13.1.0",
|
||||
"jquery": "^3.7.1",
|
||||
@@ -173,17 +169,15 @@
|
||||
"rc-trigger": "^5.3.4",
|
||||
"re-resizable": "^6.10.1",
|
||||
"react": "^17.0.2",
|
||||
"react-ace": "^10.1.0",
|
||||
"react-checkbox-tree": "^1.8.0",
|
||||
"react-color": "^2.13.8",
|
||||
"react-diff-viewer-continued": "^3.4.0",
|
||||
"react-dnd": "^11.1.3",
|
||||
"react-dnd-html5-backend": "^11.1.3",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-draggable": "^4.4.6",
|
||||
"react-google-recaptcha": "^3.1.0",
|
||||
"react-hot-loader": "^4.13.1",
|
||||
"react-intersection-observer": "^9.16.0",
|
||||
"react-js-cron": "^2.1.2",
|
||||
"react-json-tree": "^0.17.0",
|
||||
"react-lines-ellipsis": "^0.15.4",
|
||||
"react-loadable": "^5.5.0",
|
||||
@@ -197,18 +191,17 @@
|
||||
"react-syntax-highlighter": "^15.4.5",
|
||||
"react-table": "^7.8.0",
|
||||
"react-transition-group": "^4.4.5",
|
||||
"react-ultimate-pagination": "^1.3.2",
|
||||
"react-virtualized-auto-sizer": "^1.0.25",
|
||||
"react-window": "^1.8.10",
|
||||
"redux": "^4.2.1",
|
||||
"redux-localstorage": "^0.4.1",
|
||||
"redux-thunk": "^2.1.0",
|
||||
"redux-undo": "^1.0.0-beta9-9-7",
|
||||
"regenerator-runtime": "^0.14.1",
|
||||
"rimraf": "^6.0.1",
|
||||
"rison": "^0.1.1",
|
||||
"scroll-into-view-if-needed": "^3.1.0",
|
||||
"simple-zstd": "^1.4.2",
|
||||
"stream-browserify": "^3.0.0",
|
||||
"tinycolor2": "^1.4.2",
|
||||
"urijs": "^1.19.8",
|
||||
"use-event-callback": "^0.1.0",
|
||||
@@ -230,7 +223,7 @@
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
||||
"@babel/plugin-transform-modules-commonjs": "^7.26.3",
|
||||
"@babel/plugin-transform-runtime": "^7.27.1",
|
||||
"@babel/preset-env": "^7.26.7",
|
||||
"@babel/preset-env": "^7.27.2",
|
||||
"@babel/preset-react": "^7.26.3",
|
||||
"@babel/preset-typescript": "^7.26.0",
|
||||
"@babel/register": "^7.23.7",
|
||||
@@ -260,14 +253,13 @@
|
||||
"@testing-library/user-event": "^12.8.3",
|
||||
"@types/classnames": "^2.2.10",
|
||||
"@types/dom-to-image": "^2.6.7",
|
||||
"@types/enzyme": "^3.10.18",
|
||||
"@types/fetch-mock": "^7.3.2",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/jquery": "^3.5.8",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/js-levenshtein": "^1.1.3",
|
||||
"@types/json-bigint": "^1.0.4",
|
||||
"@types/math-expression-evaluator": "^1.3.3",
|
||||
"@types/mousetrap": "^1.6.15",
|
||||
"@types/node": "^22.12.0",
|
||||
"@types/react": "^17.0.83",
|
||||
"@types/react-dom": "^17.0.26",
|
||||
"@types/react-gravatar": "^2.6.14",
|
||||
@@ -277,7 +269,6 @@
|
||||
"@types/react-resizable": "^3.0.8",
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"@types/react-syntax-highlighter": "^15.5.13",
|
||||
"@types/react-table": "^7.7.20",
|
||||
"@types/react-transition-group": "^4.4.12",
|
||||
"@types/react-ultimate-pagination": "^1.2.4",
|
||||
"@types/react-virtualized-auto-sizer": "^1.0.4",
|
||||
@@ -286,13 +277,13 @@
|
||||
"@types/redux-mock-store": "^1.0.6",
|
||||
"@types/rison": "0.1.0",
|
||||
"@types/sinon": "^17.0.3",
|
||||
"@types/testing-library__jest-dom": "^5.14.9",
|
||||
"@types/tinycolor2": "^1.4.3",
|
||||
"@types/yargs": "12 - 18",
|
||||
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
||||
"@typescript-eslint/parser": "^5.62.0",
|
||||
"@wojtekmaj/enzyme-adapter-react-17": "^0.8.0",
|
||||
"babel-jest": "^29.7.0",
|
||||
"babel-loader": "^9.1.3",
|
||||
"babel-loader": "^10.0.0",
|
||||
"babel-plugin-dynamic-import-node": "^2.3.3",
|
||||
"babel-plugin-jsx-remove-data-test-id": "^3.0.0",
|
||||
"babel-plugin-lodash": "^3.3.4",
|
||||
@@ -302,11 +293,10 @@
|
||||
"cross-env": "^7.0.3",
|
||||
"css-loader": "^7.1.2",
|
||||
"css-minimizer-webpack-plugin": "^7.0.2",
|
||||
"enzyme": "^3.11.0",
|
||||
"enzyme-matchers": "^7.1.2",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-airbnb": "^19.0.4",
|
||||
"eslint-config-prettier": "^7.2.0",
|
||||
"eslint-import-resolver-alias": "^1.1.2",
|
||||
"eslint-import-resolver-typescript": "^3.7.0",
|
||||
"eslint-plugin-cypress": "^3.6.0",
|
||||
"eslint-plugin-file-progress": "^1.5.0",
|
||||
@@ -330,13 +320,13 @@
|
||||
"html-webpack-plugin": "^5.6.3",
|
||||
"imports-loader": "^5.0.0",
|
||||
"jest": "^29.7.0",
|
||||
"jest-environment-enzyme": "^7.1.2",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"jest-enzyme": "^7.1.2",
|
||||
"jest-html-reporter": "^3.10.2",
|
||||
"jest-websocket-mock": "^2.5.0",
|
||||
"jsdom": "^26.0.0",
|
||||
"lerna": "^8.2.1",
|
||||
"less": "^4.2.0",
|
||||
"less-loader": "^12.2.0",
|
||||
"mini-css-extract-plugin": "^2.9.0",
|
||||
"open-cli": "^8.0.0",
|
||||
"po2json": "^0.4.5",
|
||||
@@ -355,6 +345,7 @@
|
||||
"ts-jest": "^29.2.5",
|
||||
"ts-loader": "^9.5.1",
|
||||
"tscw-config": "^1.1.2",
|
||||
"tsx": "^4.19.2",
|
||||
"typescript": "5.1.6",
|
||||
"vm-browserify": "^1.1.2",
|
||||
"webpack": "^5.98.0",
|
||||
@@ -365,6 +356,12 @@
|
||||
"webpack-sources": "^3.2.3",
|
||||
"webpack-visualizer-plugin2": "^1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"ace-builds": "^1.41.0",
|
||||
"core-js": "^3.38.1",
|
||||
"react-ace": "^10.1.0",
|
||||
"regenerator-runtime": "^0.14.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.16.0",
|
||||
"npm": "^10.8.1"
|
||||
@@ -374,7 +371,8 @@
|
||||
"d3-color": "^3.1.0",
|
||||
"puppeteer": "^22.4.1",
|
||||
"underscore": "^1.13.7",
|
||||
"jspdf": "^3.0.1"
|
||||
"jspdf": "^3.0.1",
|
||||
"nwsapi": "^2.2.13"
|
||||
},
|
||||
"readme": "ERROR: No README data found!",
|
||||
"scarfSettings": {
|
||||
|
||||
@@ -4194,7 +4194,7 @@
|
||||
"peerDependencies": {
|
||||
"@emotion/react": "^11.4.1",
|
||||
"@types/react": "*",
|
||||
"antd": "^4.9.4",
|
||||
"antd": "^5.24.6",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1"
|
||||
}
|
||||
|
||||
@@ -25,29 +25,14 @@ import { <%= packageLabel %>Props, <%= packageLabel %>StylesProps } from './type
|
||||
|
||||
// Theming variables are provided for your use via a ThemeProvider
|
||||
// imported from @superset-ui/core. For variables available, please visit
|
||||
// https://github.com/apache-superset/superset-ui/blob/master/packages/superset-ui-core/src/style/index.ts
|
||||
// https://github.com/apache-superset/superset-ui/blob/master/packages/superset-ui-core/src/theme/index.ts
|
||||
|
||||
const Styles = styled.div<<%= packageLabel %>StylesProps>`
|
||||
background-color: ${({ theme }) => theme.colors.secondary.light2};
|
||||
padding: ${({ theme }) => theme.gridUnit * 4}px;
|
||||
border-radius: ${({ theme }) => theme.gridUnit * 2}px;
|
||||
background-color: ${({ theme }) => theme.colors.primary.light2};
|
||||
padding: ${({ theme }) => theme.sizeUnit * 4}px;
|
||||
border-radius: ${({ theme }) => theme.borderRadius}px;
|
||||
height: ${({ height }) => height}px;
|
||||
width: ${({ width }) => width}px;
|
||||
|
||||
h3 {
|
||||
/* You can use your props to control CSS! */
|
||||
margin-top: 0;
|
||||
margin-bottom: ${({ theme }) => theme.gridUnit * 3}px;
|
||||
font-size: ${({ theme, headerFontSize }) =>
|
||||
theme.typography.sizes[headerFontSize]}px;
|
||||
font-weight: ${({ theme, boldText }) =>
|
||||
theme.typography.weights[boldText ? 'bold' : 'normal']};
|
||||
}
|
||||
|
||||
pre {
|
||||
height: ${({ theme, headerFontSize, height }) =>
|
||||
height - theme.gridUnit * 12 - theme.typography.sizes[headerFontSize]}px;
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"@react-icons/all-files": "^4.1.0",
|
||||
"@types/enzyme": "^3.10.18",
|
||||
"@types/react": "*",
|
||||
"lodash": "^4.17.21",
|
||||
"prop-types": "^15.8.1"
|
||||
@@ -40,7 +39,6 @@
|
||||
"@testing-library/react-hooks": "*",
|
||||
"@testing-library/user-event": "*",
|
||||
"ace-builds": "^1.4.14",
|
||||
"antd": "4.10.3",
|
||||
"brace": "^0.11.1",
|
||||
"memoize-one": "^5.1.1",
|
||||
"react": "^17.0.2",
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
import { kebabCase } from 'lodash';
|
||||
import { t, useTheme, styled } from '@superset-ui/core';
|
||||
import Tooltip from './Tooltip';
|
||||
import { Tooltip } from '@superset-ui/core/components';
|
||||
|
||||
interface CertifiedIconWithTooltipProps {
|
||||
certifiedBy?: string | null;
|
||||
@@ -27,7 +27,7 @@ interface CertifiedIconWithTooltipProps {
|
||||
}
|
||||
|
||||
const StyledDiv = styled.div`
|
||||
margin-bottom: ${({ theme }) => theme.gridUnit * 2}px;
|
||||
margin-bottom: ${({ theme }) => theme.sizeUnit * 2}px;
|
||||
`;
|
||||
|
||||
function CertifiedIconWithTooltip({
|
||||
@@ -58,7 +58,7 @@ function CertifiedIconWithTooltip({
|
||||
>
|
||||
<g>
|
||||
<path
|
||||
fill={theme.colors.primary.base}
|
||||
fill={theme.colorPrimary}
|
||||
d="M23,12l-2.44-2.79l0.34-3.69l-3.61-0.82L15.4,1.5L12,2.96L8.6,1.5L6.71,4.69L3.1,5.5L3.44,9.2L1,12l2.44,2.79l-0.34,3.7 l3.61,0.82L8.6,22.5l3.4-1.47l3.4,1.46l1.89-3.19l3.61-0.82l-0.34-3.69L23,12z M9.38,16.01L7,13.61c-0.39-0.39-0.39-1.02,0-1.41 l0.07-0.07c0.39-0.39,1.03-0.39,1.42,0l1.61,1.62l5.15-5.16c0.39-0.39,1.03-0.39,1.42,0l0.07,0.07c0.39,0.39,0.39,1.02,0,1.41 l-5.92,5.94C10.41,16.4,9.78,16.4,9.38,16.01z"
|
||||
/>
|
||||
</g>
|
||||
|
||||
@@ -17,8 +17,12 @@
|
||||
* under the License.
|
||||
*/
|
||||
import { useState, ReactNode, useLayoutEffect, RefObject } from 'react';
|
||||
import { css, SafeMarkdown, styled, SupersetTheme } from '@superset-ui/core';
|
||||
import { Tooltip } from './Tooltip';
|
||||
import { css, styled, SupersetTheme } from '@superset-ui/core';
|
||||
import {
|
||||
SafeMarkdown,
|
||||
Tooltip,
|
||||
InfoTooltip,
|
||||
} from '@superset-ui/core/components';
|
||||
import { ColumnTypeLabel } from './ColumnTypeLabel/ColumnTypeLabel';
|
||||
import CertifiedIconWithTooltip from './CertifiedIconWithTooltip';
|
||||
import { ColumnMeta } from '../types';
|
||||
@@ -28,7 +32,6 @@ import {
|
||||
getColumnTypeTooltipNode,
|
||||
} from './labelUtils';
|
||||
import { SQLPopover } from './SQLPopover';
|
||||
import InfoTooltipWithTrigger from './InfoTooltipWithTrigger';
|
||||
|
||||
export type ColumnOptionProps = {
|
||||
column: ColumnMeta;
|
||||
@@ -40,7 +43,7 @@ const StyleOverrides = styled.span`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
svg {
|
||||
margin-right: ${({ theme }) => theme.gridUnit}px;
|
||||
margin-right: ${({ theme }) => theme.sizeUnit}px;
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -82,7 +85,7 @@ export function ColumnOption({
|
||||
<span
|
||||
className="option-label column-option-label"
|
||||
css={(theme: SupersetTheme) => css`
|
||||
margin-right: ${theme.gridUnit}px;
|
||||
margin-right: ${theme.sizeUnit}px;
|
||||
`}
|
||||
ref={labelRef}
|
||||
>
|
||||
@@ -98,15 +101,13 @@ export function ColumnOption({
|
||||
/>
|
||||
)}
|
||||
{warningMarkdown && (
|
||||
<InfoTooltipWithTrigger
|
||||
className="text-warning"
|
||||
icon="warning"
|
||||
<InfoTooltip
|
||||
type="warning"
|
||||
tooltip={<SafeMarkdown source={warningMarkdown} />}
|
||||
label={`warn-${column.column_name}`}
|
||||
iconsStyle={{ marginLeft: 0 }}
|
||||
iconStyle={{ marginLeft: 0 }}
|
||||
{...(column.error_text && {
|
||||
className: 'text-danger',
|
||||
icon: 'exclamation-circle',
|
||||
type: 'error',
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -39,9 +39,9 @@ const TypeIconWrapper = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: ${theme.gridUnit * 6}px;
|
||||
height: ${theme.gridUnit * 6}px;
|
||||
margin-right: ${theme.gridUnit}px;
|
||||
width: ${theme.sizeUnit * 6}px;
|
||||
height: ${theme.sizeUnit * 6}px;
|
||||
margin-right: ${theme.sizeUnit}px;
|
||||
|
||||
&& svg {
|
||||
margin-right: 0;
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
* under the License.
|
||||
*/
|
||||
import { ReactNode } from 'react';
|
||||
import { t } from '@superset-ui/core';
|
||||
import { InfoTooltipWithTrigger } from './InfoTooltipWithTrigger';
|
||||
import { Tooltip } from './Tooltip';
|
||||
import { t, css } from '@superset-ui/core';
|
||||
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||
import { InfoTooltip, Tooltip } from '@superset-ui/core/components';
|
||||
|
||||
type ValidationError = string;
|
||||
|
||||
@@ -60,7 +60,7 @@ export function ControlHeader({
|
||||
<span>
|
||||
{description && (
|
||||
<span>
|
||||
<InfoTooltipWithTrigger
|
||||
<InfoTooltip
|
||||
label={t('description')}
|
||||
tooltip={description}
|
||||
placement="top"
|
||||
@@ -70,11 +70,11 @@ export function ControlHeader({
|
||||
)}
|
||||
{renderTrigger && (
|
||||
<span>
|
||||
<InfoTooltipWithTrigger
|
||||
<InfoTooltip
|
||||
label={t('bolt')}
|
||||
tooltip={t('Changing this control takes effect instantly')}
|
||||
placement="top"
|
||||
icon="bolt"
|
||||
type="notice"
|
||||
/>{' '}
|
||||
</span>
|
||||
)}
|
||||
@@ -88,6 +88,7 @@ export function ControlHeader({
|
||||
return null;
|
||||
}
|
||||
const labelClass = validationErrors.length > 0 ? 'text-danger' : '';
|
||||
|
||||
return (
|
||||
<div className="ControlHeader" data-test={`${name}-header`}>
|
||||
<div className="pull-left">
|
||||
@@ -98,25 +99,30 @@ export function ControlHeader({
|
||||
tabIndex={0}
|
||||
onClick={onClick}
|
||||
className={labelClass}
|
||||
style={{ cursor: onClick ? 'pointer' : '' }}
|
||||
>
|
||||
{label}
|
||||
</span>{' '}
|
||||
{warning && (
|
||||
<span>
|
||||
<Tooltip id="error-tooltip" placement="top" title={warning}>
|
||||
{/* TODO: Remove fa-icon */}
|
||||
{/* eslint-disable-next-line icons/no-fa-icons-usage */}
|
||||
<i className="fa fa-exclamation-circle text-warning" />
|
||||
<InfoCircleOutlined
|
||||
css={theme => css`
|
||||
font-size: ${theme.sizeUnit * 3}px;
|
||||
color: ${theme.colorError};
|
||||
`}
|
||||
/>
|
||||
</Tooltip>{' '}
|
||||
</span>
|
||||
)}
|
||||
{danger && (
|
||||
<span>
|
||||
<Tooltip id="error-tooltip" placement="top" title={danger}>
|
||||
{/* TODO: Remove fa-icon */}
|
||||
{/* eslint-disable-next-line icons/no-fa-icons-usage */}
|
||||
<i className="fa fa-exclamation-circle text-danger" />
|
||||
<InfoCircleOutlined
|
||||
css={theme => css`
|
||||
font-size: ${theme.sizeUnit * 3}px;
|
||||
color: ${theme.colorError};
|
||||
`}
|
||||
/>{' '}
|
||||
</Tooltip>{' '}
|
||||
</span>
|
||||
)}
|
||||
@@ -127,18 +133,17 @@ export function ControlHeader({
|
||||
placement="top"
|
||||
title={validationErrors.join(' ')}
|
||||
>
|
||||
{/* TODO: Remove fa-icon */}
|
||||
{/* eslint-disable-next-line icons/no-fa-icons-usage */}
|
||||
<i className="fa fa-exclamation-circle text-danger" />
|
||||
<InfoCircleOutlined
|
||||
css={theme => css`
|
||||
font-size: ${theme.sizeUnit * 3}px;
|
||||
color: ${theme.colorError};
|
||||
`}
|
||||
/>{' '}
|
||||
</Tooltip>{' '}
|
||||
</span>
|
||||
)}
|
||||
{renderOptionalIcons()}
|
||||
{required && (
|
||||
<span className="text-danger m-l-4">
|
||||
<strong>*</strong>
|
||||
</span>
|
||||
)}
|
||||
{required && <strong> *</strong>}
|
||||
</label>
|
||||
</div>
|
||||
{rightNode && <div className="pull-right">{rightNode}</div>}
|
||||
|
||||
@@ -20,9 +20,8 @@ import { styled, css } from '@superset-ui/core';
|
||||
|
||||
export const ControlSubSectionHeader = styled.div`
|
||||
${({ theme }) => css`
|
||||
font-weight: ${theme.typography.weights.bold};
|
||||
font-size: ${theme.typography.sizes.s};
|
||||
margin-bottom: ${theme.gridUnit}px;
|
||||
font-weight: ${theme.fontWeightStrong};
|
||||
margin-bottom: ${theme.sizeUnit}px;
|
||||
font-size: ${theme.fontSizeSM}px;
|
||||
`}
|
||||
`;
|
||||
export default ControlSubSectionHeader;
|
||||
|
||||
@@ -17,5 +17,4 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
export { Dropdown } from 'antd';
|
||||
export type { DropDownProps } from 'antd/lib/dropdown';
|
||||
export { Dropdown, type DropdownProps } from '@superset-ui/core/components';
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { CSSProperties } from 'react';
|
||||
import { kebabCase } from 'lodash';
|
||||
import { t } from '@superset-ui/core';
|
||||
import { Tooltip, TooltipProps, TooltipPlacement } from './Tooltip';
|
||||
|
||||
export interface InfoTooltipWithTriggerProps {
|
||||
label?: string;
|
||||
tooltip?: TooltipProps['title'];
|
||||
icon?: string;
|
||||
onClick?: () => void;
|
||||
placement?: TooltipPlacement;
|
||||
bsStyle?: string;
|
||||
className?: string;
|
||||
iconsStyle?: CSSProperties;
|
||||
}
|
||||
|
||||
export function InfoTooltipWithTrigger({
|
||||
label,
|
||||
tooltip,
|
||||
bsStyle,
|
||||
onClick,
|
||||
icon = 'info-circle',
|
||||
className = 'text-muted',
|
||||
placement = 'right',
|
||||
iconsStyle = {},
|
||||
}: InfoTooltipWithTriggerProps) {
|
||||
const iconClass = `fa fa-${icon} ${className} ${
|
||||
bsStyle ? `text-${bsStyle}` : ''
|
||||
}`;
|
||||
const iconEl = (
|
||||
<i
|
||||
role="button"
|
||||
aria-label={t('Show info tooltip')}
|
||||
tabIndex={0}
|
||||
className={iconClass}
|
||||
style={{ cursor: onClick ? 'pointer' : undefined, ...iconsStyle }}
|
||||
onClick={onClick}
|
||||
onKeyPress={
|
||||
onClick &&
|
||||
(event => {
|
||||
if (event.key === 'Enter' || event.key === ' ') {
|
||||
onClick();
|
||||
}
|
||||
})
|
||||
}
|
||||
/>
|
||||
);
|
||||
if (!tooltip) {
|
||||
return iconEl;
|
||||
}
|
||||
return (
|
||||
<Tooltip
|
||||
id={`${kebabCase(label)}-tooltip`}
|
||||
title={tooltip}
|
||||
placement={placement}
|
||||
>
|
||||
{iconEl}
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
export default InfoTooltipWithTrigger;
|
||||
@@ -17,5 +17,4 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
export { Menu } from 'antd';
|
||||
export type { MenuProps } from 'antd/lib/menu';
|
||||
export { Menu, type MenuProps } from '@superset-ui/core/components';
|
||||
|
||||
@@ -18,17 +18,16 @@
|
||||
*/
|
||||
import { useState, ReactNode, useLayoutEffect, RefObject } from 'react';
|
||||
|
||||
import { css, styled, Metric, SupersetTheme } from '@superset-ui/core';
|
||||
import {
|
||||
css,
|
||||
styled,
|
||||
Metric,
|
||||
SafeMarkdown,
|
||||
SupersetTheme,
|
||||
} from '@superset-ui/core';
|
||||
import InfoTooltipWithTrigger from './InfoTooltipWithTrigger';
|
||||
Typography,
|
||||
// TODO: somehow doesn't work with our main Tooltip (?)
|
||||
RawAntdTooltip as Tooltip,
|
||||
InfoTooltip,
|
||||
} from '@superset-ui/core/components';
|
||||
import { ColumnTypeLabel } from './ColumnTypeLabel/ColumnTypeLabel';
|
||||
import CertifiedIconWithTooltip from './CertifiedIconWithTooltip';
|
||||
import Tooltip from './Tooltip';
|
||||
import { getMetricTooltipNode } from './labelUtils';
|
||||
import { SQLPopover } from './SQLPopover';
|
||||
|
||||
@@ -37,7 +36,7 @@ const FlexRowContainer = styled.div`
|
||||
display: flex;
|
||||
|
||||
> svg {
|
||||
margin-right: ${({ theme }) => theme.gridUnit}px;
|
||||
margin-right: ${({ theme }) => theme.sizeUnit}px;
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -61,23 +60,26 @@ export function MetricOption({
|
||||
url = '',
|
||||
}: MetricOptionProps) {
|
||||
const verbose = metric.verbose_name || metric.metric_name || metric.label;
|
||||
const link = url ? (
|
||||
<a href={url} target={openInNewWindow ? '_blank' : ''} rel="noreferrer">
|
||||
{verbose}
|
||||
</a>
|
||||
) : (
|
||||
verbose
|
||||
);
|
||||
|
||||
const label = (
|
||||
<span
|
||||
className="option-label metric-option-label"
|
||||
css={(theme: SupersetTheme) => css`
|
||||
margin-right: ${theme.gridUnit}px;
|
||||
margin-right: ${theme.sizeUnit}px;
|
||||
`}
|
||||
ref={labelRef}
|
||||
>
|
||||
{link}
|
||||
{url ? (
|
||||
<Typography.Link
|
||||
href={url}
|
||||
target={openInNewWindow ? '_blank' : ''}
|
||||
rel="noreferrer"
|
||||
>
|
||||
{verbose}
|
||||
</Typography.Link>
|
||||
) : (
|
||||
verbose
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
|
||||
@@ -111,15 +113,13 @@ export function MetricOption({
|
||||
/>
|
||||
)}
|
||||
{warningMarkdown && (
|
||||
<InfoTooltipWithTrigger
|
||||
className="text-warning"
|
||||
icon="warning"
|
||||
<InfoTooltip
|
||||
type="warning"
|
||||
tooltip={<SafeMarkdown source={warningMarkdown} />}
|
||||
label={`warn-${metric.metric_name}`}
|
||||
iconsStyle={{ marginLeft: 0 }}
|
||||
iconStyle={{ marginLeft: 0 }}
|
||||
{...(metric.error_text && {
|
||||
className: 'text-danger',
|
||||
icon: 'exclamation-circle',
|
||||
type: 'error',
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -17,19 +17,18 @@
|
||||
* under the License.
|
||||
*/
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Popover } from 'antd-v5';
|
||||
import { Popover, type PopoverProps } from '@superset-ui/core/components';
|
||||
import type ReactAce from 'react-ace';
|
||||
import type { PopoverProps } from 'antd-v5/lib/popover';
|
||||
import { CalculatorOutlined } from '@ant-design/icons';
|
||||
import { css, styled, useTheme, t } from '@superset-ui/core';
|
||||
|
||||
const StyledCalculatorIcon = styled(CalculatorOutlined)`
|
||||
${({ theme }) => css`
|
||||
color: ${theme.colors.grayscale.base};
|
||||
font-size: ${theme.typography.sizes.s}px;
|
||||
font-size: ${theme.fontSizeSM}px;
|
||||
& svg {
|
||||
margin-left: ${theme.gridUnit}px;
|
||||
margin-right: ${theme.gridUnit}px;
|
||||
margin-left: ${theme.sizeUnit}px;
|
||||
margin-right: ${theme.sizeUnit}px;
|
||||
}
|
||||
`}
|
||||
`;
|
||||
@@ -65,9 +64,9 @@ export const SQLPopover = (props: PopoverProps & { sqlExpression: string }) => {
|
||||
readOnly
|
||||
wrapEnabled
|
||||
style={{
|
||||
border: `1px solid ${theme.colors.grayscale.light2}`,
|
||||
background: theme.colors.secondary.light5,
|
||||
maxWidth: theme.gridUnit * 100,
|
||||
border: `1px solid ${theme.colorBorder}`,
|
||||
background: theme.colorPrimaryBg,
|
||||
maxWidth: theme.sizeUnit * 100,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
|
||||
@@ -17,7 +17,10 @@
|
||||
* under the License.
|
||||
*/
|
||||
import { useState, ReactNode } from 'react';
|
||||
import AntdSelect, { SelectProps as AntdSelectProps } from 'antd/lib/select';
|
||||
import {
|
||||
RawAntdSelect as AntdSelect,
|
||||
type RawAntdSelectProps as AntdSelectProps,
|
||||
} from '@superset-ui/core/components';
|
||||
|
||||
export const { Option }: any = AntdSelect;
|
||||
|
||||
@@ -35,7 +38,7 @@ export type SelectProps<VT> = Omit<AntdSelectProps<VT>, 'options'> & {
|
||||
export default function Select<VT extends string | number>({
|
||||
creatable,
|
||||
onSearch,
|
||||
dropdownMatchSelectWidth = false,
|
||||
popupMatchSelectWidth = false,
|
||||
minWidth = '100%',
|
||||
showSearch: showSearch_ = true,
|
||||
onChange,
|
||||
@@ -73,7 +76,7 @@ export default function Select<VT extends string | number>({
|
||||
|
||||
return (
|
||||
<AntdSelect<VT>
|
||||
dropdownMatchSelectWidth={dropdownMatchSelectWidth}
|
||||
popupMatchSelectWidth={popupMatchSelectWidth}
|
||||
showSearch={showSearch}
|
||||
onSearch={handleSearch}
|
||||
onChange={handleChange}
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { useTheme } from '@superset-ui/core';
|
||||
import { Tooltip as BaseTooltip } from 'antd-v5';
|
||||
import {
|
||||
TooltipProps as BaseTooltipProps,
|
||||
TooltipPlacement as BaseTooltipPlacement,
|
||||
} from 'antd-v5/lib/tooltip';
|
||||
|
||||
export type TooltipProps = BaseTooltipProps;
|
||||
export type TooltipPlacement = BaseTooltipPlacement;
|
||||
|
||||
export const Tooltip = ({
|
||||
overlayStyle = {},
|
||||
color,
|
||||
...props
|
||||
}: BaseTooltipProps) => {
|
||||
const theme = useTheme();
|
||||
const defaultColor = `${theme.colors.grayscale.dark2}e6`;
|
||||
return (
|
||||
<BaseTooltip
|
||||
styles={{
|
||||
root: {
|
||||
fontSize: theme.typography.sizes.s,
|
||||
lineHeight: '1.6',
|
||||
maxWidth: theme.gridUnit * 62,
|
||||
minWidth: theme.gridUnit * 30,
|
||||
...overlayStyle,
|
||||
},
|
||||
}}
|
||||
// make the tooltip display closer to the label
|
||||
align={{ offset: [0, 1] }}
|
||||
color={defaultColor || color}
|
||||
trigger="hover"
|
||||
placement="bottom"
|
||||
// don't allow hovering over the tooltip
|
||||
mouseLeaveDelay={0}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default Tooltip;
|
||||
@@ -29,18 +29,18 @@ const TooltipSectionWrapper = styled.div`
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
font-size: ${theme.typography.sizes.s}px;
|
||||
font-size: ${theme.fontSizeSM}px;
|
||||
line-height: 1.2;
|
||||
|
||||
&:not(:last-of-type) {
|
||||
margin-bottom: ${theme.gridUnit * 2}px;
|
||||
margin-bottom: ${theme.sizeUnit * 2}px;
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
const TooltipSectionLabel = styled.span`
|
||||
${({ theme }) => css`
|
||||
font-weight: ${theme.typography.weights.bold};
|
||||
font-weight: ${theme.fontWeightStrong};
|
||||
`}
|
||||
`;
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
import { ColumnMeta, SortSeriesData, SortSeriesType } from './types';
|
||||
|
||||
export const DEFAULT_MAX_ROW = 100000;
|
||||
export const DEFAULT_MAX_ROW_TABLE_SERVER = 500000;
|
||||
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const TIME_FILTER_LABELS = {
|
||||
|
||||
@@ -25,14 +25,12 @@ export * from './operators';
|
||||
// can't do `export * as sections from './sections'`, babel-transformer will fail
|
||||
export const sections = sectionsModule;
|
||||
|
||||
export * from './components/InfoTooltipWithTrigger';
|
||||
export * from './components/ColumnOption';
|
||||
export * from './components/ColumnTypeLabel/ColumnTypeLabel';
|
||||
export * from './components/ControlSubSectionHeader';
|
||||
export * from './components/Dropdown';
|
||||
export * from './components/Menu';
|
||||
export * from './components/MetricOption';
|
||||
export * from './components/Tooltip';
|
||||
export * from './components/ControlHeader';
|
||||
|
||||
export * from './shared-controls';
|
||||
|
||||
@@ -30,7 +30,7 @@ export const aggregationOperator: PostProcessingFactory<
|
||||
> = (formData: QueryFormData, queryObject) => {
|
||||
const { aggregation = 'LAST_VALUE' } = formData;
|
||||
|
||||
if (aggregation === 'LAST_VALUE') {
|
||||
if (aggregation === 'LAST_VALUE' || aggregation === 'raw') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
* under the License.
|
||||
*/
|
||||
import { ReactNode } from 'react';
|
||||
import { JsonValue, useTheme } from '@superset-ui/core';
|
||||
import { JsonValue } from '@superset-ui/core';
|
||||
import { Radio } from '@superset-ui/core/components';
|
||||
import { ControlHeader } from '../../components/ControlHeader';
|
||||
|
||||
// [value, label]
|
||||
@@ -42,51 +43,19 @@ export default function RadioButtonControl({
|
||||
...props
|
||||
}: RadioButtonControlProps) {
|
||||
const currentValue = initialValue || options[0][0];
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<div
|
||||
css={{
|
||||
'.btn svg': {
|
||||
position: 'relative',
|
||||
top: '0.2em',
|
||||
},
|
||||
'.btn:focus': {
|
||||
outline: 'none',
|
||||
},
|
||||
'.control-label': {
|
||||
color: theme.colors.grayscale.base,
|
||||
marginBottom: theme.gridUnit,
|
||||
},
|
||||
'.control-label + .btn-group': {
|
||||
marginTop: '1px',
|
||||
},
|
||||
'.btn-group .btn-default': {
|
||||
color: theme.colors.grayscale.dark1,
|
||||
},
|
||||
'.btn-group .btn.active': {
|
||||
background: theme.colors.grayscale.light4,
|
||||
fontWeight: theme.typography.weights.bold,
|
||||
boxShadow: 'none',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<ControlHeader {...props} />
|
||||
<div className="btn-group btn-group-sm">
|
||||
<Radio.Group
|
||||
value={currentValue}
|
||||
onChange={e => onChange(e.target.value)}
|
||||
>
|
||||
{options.map(([val, label]) => (
|
||||
<button
|
||||
key={JSON.stringify(val)}
|
||||
type="button"
|
||||
className={`btn btn-default ${
|
||||
val === currentValue ? 'active' : ''
|
||||
}`}
|
||||
onClick={() => {
|
||||
onChange(val);
|
||||
}}
|
||||
>
|
||||
<Radio.Button key={JSON.stringify(val)} value={val}>
|
||||
{label}
|
||||
</button>
|
||||
</Radio.Button>
|
||||
))}
|
||||
</div>
|
||||
</Radio.Group>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ export const aggregationControl = {
|
||||
clearable: false,
|
||||
renderTrigger: false,
|
||||
choices: [
|
||||
['raw', t('None')],
|
||||
['LAST_VALUE', t('Last Value')],
|
||||
['sum', t('Total (Sum)')],
|
||||
['mean', t('Average (Mean)')],
|
||||
@@ -77,7 +78,9 @@ export const aggregationControl = {
|
||||
['max', t('Maximum')],
|
||||
['median', t('Median')],
|
||||
],
|
||||
description: t('Select an aggregation method to apply to the metric.'),
|
||||
description: t(
|
||||
'Aggregation method used to compute the Big Number from the Trendline.For non-additive metrics like ratios, averages, distinct counts, etc use NONE.',
|
||||
),
|
||||
provideFormDataToProps: true,
|
||||
mapStateToProps: ({ form_data }: ControlPanelState) => ({
|
||||
value: form_data.aggregation || 'LAST_VALUE',
|
||||
|
||||
@@ -69,6 +69,7 @@ export interface Dataset {
|
||||
columns: ColumnMeta[];
|
||||
metrics: Metric[];
|
||||
column_formats: Record<string, string>;
|
||||
currency_formats?: Record<string, Currency>;
|
||||
verbose_map: Record<string, string>;
|
||||
main_dttm_col: string;
|
||||
// eg. ['["ds", true]', 'ds [asc]']
|
||||
@@ -300,7 +301,7 @@ export interface FilterOption<T extends SelectOption> {
|
||||
data: T;
|
||||
}
|
||||
|
||||
// Ref: superset-frontend/src/components/Select/SupersetStyledSelect.tsx
|
||||
// Ref: superset-frontend/@superset-ui/core/components/Select/SupersetStyledSelect.tsx
|
||||
export interface SelectControlConfig<
|
||||
O extends SelectOption = SelectOption,
|
||||
T extends SelectControlType = SelectControlType,
|
||||
|
||||
@@ -17,26 +17,26 @@
|
||||
* under the License.
|
||||
*/
|
||||
import '@testing-library/jest-dom';
|
||||
import { render } from '@testing-library/react';
|
||||
import {
|
||||
ThemeProvider,
|
||||
supersetTheme,
|
||||
GenericDataType,
|
||||
} from '@superset-ui/core';
|
||||
import { render } from '@superset-ui/core/spec';
|
||||
import { GenericDataType } from '@superset-ui/core';
|
||||
|
||||
import { ColumnOption, ColumnOptionProps } from '../../src';
|
||||
|
||||
jest.mock('../../src/components/SQLPopover', () => ({
|
||||
jest.mock('@superset-ui/chart-controls/components/SQLPopover', () => ({
|
||||
SQLPopover: () => <div data-test="mock-sql-popover" />,
|
||||
}));
|
||||
jest.mock('../../src/components/ColumnTypeLabel/ColumnTypeLabel', () => ({
|
||||
ColumnTypeLabel: ({ type }: { type: string }) => (
|
||||
<div data-test="mock-column-type-label">{type}</div>
|
||||
),
|
||||
jest.mock(
|
||||
'@superset-ui/chart-controls/components/ColumnTypeLabel/ColumnTypeLabel',
|
||||
() => ({
|
||||
ColumnTypeLabel: ({ type }: { type: string }) => (
|
||||
<div data-test="mock-column-type-label">{type}</div>
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
||||
jest.mock('@superset-ui/core/components/InfoTooltip', () => ({
|
||||
InfoTooltip: () => <div data-test="mock-tooltip" />,
|
||||
}));
|
||||
jest.mock('../../src/components/InfoTooltipWithTrigger', () => () => (
|
||||
<div data-test="mock-info-tooltip-with-trigger" />
|
||||
));
|
||||
|
||||
const defaultProps: ColumnOptionProps = {
|
||||
column: {
|
||||
@@ -49,11 +49,7 @@ const defaultProps: ColumnOptionProps = {
|
||||
};
|
||||
|
||||
const setup = (props: Partial<ColumnOptionProps> = {}) =>
|
||||
render(
|
||||
<ThemeProvider theme={supersetTheme}>
|
||||
<ColumnOption {...defaultProps} {...props} />
|
||||
</ThemeProvider>,
|
||||
);
|
||||
render(<ColumnOption {...defaultProps} {...props} />);
|
||||
test('shows a label with verbose_name', () => {
|
||||
const { container } = setup();
|
||||
const lbl = container.getElementsByClassName('option-label');
|
||||
@@ -114,11 +110,11 @@ test('dttm column has correct column label if showType is true', () => {
|
||||
String(GenericDataType.Temporal),
|
||||
);
|
||||
});
|
||||
test('doesnt show InfoTooltipWithTrigger when no warning', () => {
|
||||
test('doesnt show InfoTooltip when no warning', () => {
|
||||
const { queryByText } = setup();
|
||||
expect(queryByText('mock-info-tooltip-with-trigger')).not.toBeInTheDocument();
|
||||
expect(queryByText('mock-tooltip')).not.toBeInTheDocument();
|
||||
});
|
||||
test('shows a warning with InfoTooltipWithTrigger when it contains warning', () => {
|
||||
test('shows a warning with InfoTooltip when it contains warning', () => {
|
||||
const { getByTestId } = setup({
|
||||
...defaultProps,
|
||||
column: {
|
||||
@@ -126,5 +122,5 @@ test('shows a warning with InfoTooltipWithTrigger when it contains warning', ()
|
||||
warning_text: 'This is a warning',
|
||||
},
|
||||
});
|
||||
expect(getByTestId('mock-info-tooltip-with-trigger')).toBeInTheDocument();
|
||||
expect(getByTestId('mock-tooltip')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
* under the License.
|
||||
*/
|
||||
import { isValidElement } from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { render, screen } from '@superset-ui/core/spec';
|
||||
import '@testing-library/jest-dom';
|
||||
import { GenericDataType } from '@superset-ui/core';
|
||||
|
||||
|
||||
@@ -17,11 +17,10 @@
|
||||
* under the License.
|
||||
*/
|
||||
import '@testing-library/jest-dom';
|
||||
import { fireEvent, render } from '@testing-library/react';
|
||||
import { ThemeProvider, supersetTheme } from '@superset-ui/core';
|
||||
import { InfoTooltipWithTrigger, InfoTooltipWithTriggerProps } from '../../src';
|
||||
import { fireEvent, render } from '@superset-ui/core/spec';
|
||||
import { InfoTooltip, InfoTooltipProps } from '@superset-ui/core/components';
|
||||
|
||||
jest.mock('../../src/components/Tooltip', () => ({
|
||||
jest.mock('@superset-ui/core/components/Tooltip', () => ({
|
||||
Tooltip: ({ children }: { children: React.ReactNode }) => (
|
||||
<div data-test="mock-tooltip">{children}</div>
|
||||
),
|
||||
@@ -29,12 +28,8 @@ jest.mock('../../src/components/Tooltip', () => ({
|
||||
|
||||
const defaultProps = {};
|
||||
|
||||
const setup = (props: Partial<InfoTooltipWithTriggerProps> = {}) =>
|
||||
render(
|
||||
<ThemeProvider theme={supersetTheme}>
|
||||
<InfoTooltipWithTrigger {...defaultProps} {...props} />
|
||||
</ThemeProvider>,
|
||||
);
|
||||
const setup = (props: Partial<InfoTooltipProps> = {}) =>
|
||||
render(<InfoTooltip {...defaultProps} {...props} />);
|
||||
|
||||
test('renders a tooltip', () => {
|
||||
const { getAllByTestId } = setup({
|
||||
@@ -44,31 +39,29 @@ test('renders a tooltip', () => {
|
||||
expect(getAllByTestId('mock-tooltip').length).toEqual(1);
|
||||
});
|
||||
|
||||
test('renders an info icon', () => {
|
||||
const { container } = setup();
|
||||
expect(container.getElementsByClassName('fa-info-circle')).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('responds to keypresses', () => {
|
||||
test('responds to keydown events', () => {
|
||||
const clickHandler = jest.fn();
|
||||
const { getByRole } = setup({
|
||||
label: 'test',
|
||||
tooltip: 'this is a test',
|
||||
onClick: clickHandler,
|
||||
});
|
||||
fireEvent.keyPress(getByRole('button'), {
|
||||
|
||||
fireEvent.keyDown(getByRole('button'), {
|
||||
key: 'Tab',
|
||||
code: 9,
|
||||
charCode: 9,
|
||||
});
|
||||
expect(clickHandler).toHaveBeenCalledTimes(0);
|
||||
fireEvent.keyPress(getByRole('button'), {
|
||||
|
||||
fireEvent.keyDown(getByRole('button'), {
|
||||
key: 'Enter',
|
||||
code: 13,
|
||||
charCode: 13,
|
||||
});
|
||||
expect(clickHandler).toHaveBeenCalledTimes(1);
|
||||
fireEvent.keyPress(getByRole('button'), {
|
||||
|
||||
fireEvent.keyDown(getByRole('button'), {
|
||||
key: ' ',
|
||||
code: 32,
|
||||
charCode: 32,
|
||||
@@ -76,9 +69,47 @@ test('responds to keypresses', () => {
|
||||
expect(clickHandler).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
test('has a bsStyle', () => {
|
||||
test('finds the info circle icon inside info variant', () => {
|
||||
const { container } = setup({
|
||||
bsStyle: 'something',
|
||||
type: 'info',
|
||||
});
|
||||
expect(container.getElementsByClassName('text-something')).toHaveLength(1);
|
||||
|
||||
const iconSpan = container.querySelector('svg[data-icon="info-circle"]');
|
||||
expect(iconSpan).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('finds the warning icon inside warning variant', () => {
|
||||
const { container } = setup({
|
||||
type: 'warning',
|
||||
});
|
||||
|
||||
const iconSpan = container.querySelector('svg[data-icon="warning"]');
|
||||
expect(iconSpan).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('finds the close circle icon inside error variant', () => {
|
||||
const { container } = setup({
|
||||
type: 'error',
|
||||
});
|
||||
|
||||
const iconSpan = container.querySelector('svg[data-icon="close-circle"]');
|
||||
expect(iconSpan).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('finds the question circle icon inside question variant', () => {
|
||||
const { container } = setup({
|
||||
type: 'question',
|
||||
});
|
||||
|
||||
const iconSpan = container.querySelector('svg[data-icon="question-circle"]');
|
||||
expect(iconSpan).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('finds the thunderbolt icon inside notice variant', () => {
|
||||
const { container } = setup({
|
||||
type: 'notice',
|
||||
});
|
||||
|
||||
const iconSpan = container.querySelector('svg[data-icon="thunderbolt"]');
|
||||
expect(iconSpan).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -17,24 +17,30 @@
|
||||
* under the License.
|
||||
*/
|
||||
import '@testing-library/jest-dom';
|
||||
import { render } from '@testing-library/react';
|
||||
import { ThemeProvider, supersetTheme } from '@superset-ui/core';
|
||||
import { MetricOption, MetricOptionProps } from '../../src';
|
||||
import { render } from '@superset-ui/core/spec';
|
||||
import {
|
||||
MetricOption,
|
||||
MetricOptionProps,
|
||||
} from '../../src/components/MetricOption';
|
||||
|
||||
jest.mock('../../src/components/InfoTooltipWithTrigger', () => () => (
|
||||
<div data-test="mock-info-tooltip-with-trigger" />
|
||||
));
|
||||
jest.mock('../../src/components/ColumnTypeLabel/ColumnTypeLabel', () => ({
|
||||
ColumnTypeLabel: () => <div data-test="mock-column-type-label" />,
|
||||
jest.mock('@superset-ui/core/components/InfoTooltip', () => ({
|
||||
InfoTooltip: () => <div data-test="mock-tooltip" />,
|
||||
}));
|
||||
|
||||
jest.mock(
|
||||
'../../src/components/Tooltip',
|
||||
'@superset-ui/chart-controls/components/ColumnTypeLabel/ColumnTypeLabel',
|
||||
() => ({
|
||||
ColumnTypeLabel: () => <div data-test="mock-column-type-label" />,
|
||||
}),
|
||||
);
|
||||
jest.mock(
|
||||
'@superset-ui/core/components/Tooltip',
|
||||
() =>
|
||||
({ children }: { children: React.ReactNode }) => (
|
||||
<div data-test="mock-tooltip">{children}</div>
|
||||
),
|
||||
);
|
||||
jest.mock('../../src/components/SQLPopover', () => ({
|
||||
jest.mock('@superset-ui/chart-controls/components/SQLPopover', () => ({
|
||||
SQLPopover: () => <div data-test="mock-sql-popover" />,
|
||||
}));
|
||||
|
||||
@@ -54,20 +60,16 @@ const defaultProps = {
|
||||
};
|
||||
|
||||
const setup = (props: Partial<MetricOptionProps> = {}) =>
|
||||
render(
|
||||
<ThemeProvider theme={supersetTheme}>
|
||||
<MetricOption {...defaultProps} {...props} />
|
||||
</ThemeProvider>,
|
||||
);
|
||||
render(<MetricOption {...defaultProps} {...props} />);
|
||||
test('shows a label with verbose_name', () => {
|
||||
const { container } = setup();
|
||||
const lbl = container.getElementsByClassName('option-label');
|
||||
expect(lbl).toHaveLength(1);
|
||||
expect(`${lbl[0].textContent}`).toEqual(defaultProps.metric.verbose_name);
|
||||
});
|
||||
test('shows a InfoTooltipWithTrigger', () => {
|
||||
test('shows a InfoTooltip', () => {
|
||||
const { getByTestId } = setup();
|
||||
expect(getByTestId('mock-info-tooltip-with-trigger')).toBeInTheDocument();
|
||||
expect(getByTestId('mock-tooltip')).toBeInTheDocument();
|
||||
});
|
||||
test('shows SQL Popover trigger', () => {
|
||||
const { getByTestId } = setup();
|
||||
@@ -82,14 +84,14 @@ test('shows a label with metric_name when no verbose_name', () => {
|
||||
});
|
||||
expect(getByText(defaultProps.metric.metric_name)).toBeInTheDocument();
|
||||
});
|
||||
test('doesnt show InfoTooltipWithTrigger when no warning', () => {
|
||||
test('doesnt show InfoTooltip when no warning', () => {
|
||||
const { queryByText } = setup({
|
||||
metric: {
|
||||
...defaultProps.metric,
|
||||
warning_text: '',
|
||||
},
|
||||
});
|
||||
expect(queryByText('mock-info-tooltip-with-trigger')).not.toBeInTheDocument();
|
||||
expect(queryByText('mock-tooltip')).not.toBeInTheDocument();
|
||||
});
|
||||
test('sets target="_blank" when openInNewWindow is true', () => {
|
||||
const { getByRole } = setup({
|
||||
|
||||
@@ -16,10 +16,8 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { ReactElement } from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { render, screen } from '@superset-ui/core/spec';
|
||||
import '@testing-library/jest-dom';
|
||||
import { ThemeProvider, supersetTheme } from '@superset-ui/core';
|
||||
import {
|
||||
getColumnLabelText,
|
||||
getColumnTooltipNode,
|
||||
@@ -27,9 +25,6 @@ import {
|
||||
getColumnTypeTooltipNode,
|
||||
} from '../../src/components/labelUtils';
|
||||
|
||||
const renderWithTheme = (ui: ReactElement) =>
|
||||
render(<ThemeProvider theme={supersetTheme}>{ui}</ThemeProvider>);
|
||||
|
||||
test("should get column name when column doesn't have verbose_name", () => {
|
||||
expect(
|
||||
getColumnLabelText({
|
||||
@@ -78,7 +73,7 @@ test('should get null for column datatype tooltip when type is blank', () => {
|
||||
});
|
||||
|
||||
test('should get column datatype rendered as tooltip when column has a type', () => {
|
||||
renderWithTheme(
|
||||
render(
|
||||
<>
|
||||
{getColumnTypeTooltipNode({
|
||||
id: 123,
|
||||
@@ -96,7 +91,7 @@ test('should get column datatype rendered as tooltip when column has a type', ()
|
||||
|
||||
test('should get column name, verbose name and description when it has a verbose name', () => {
|
||||
const ref = { current: { scrollWidth: 100, clientWidth: 100 } };
|
||||
renderWithTheme(
|
||||
render(
|
||||
<>
|
||||
{getColumnTooltipNode(
|
||||
{
|
||||
@@ -120,7 +115,7 @@ test('should get column name, verbose name and description when it has a verbose
|
||||
|
||||
test('should get column name as tooltip if it overflowed', () => {
|
||||
const ref = { current: { scrollWidth: 200, clientWidth: 100 } };
|
||||
renderWithTheme(
|
||||
render(
|
||||
<>
|
||||
{getColumnTooltipNode(
|
||||
{
|
||||
@@ -141,7 +136,7 @@ test('should get column name as tooltip if it overflowed', () => {
|
||||
|
||||
test('should get column name, verbose name and description as tooltip if it overflowed', () => {
|
||||
const ref = { current: { scrollWidth: 200, clientWidth: 100 } };
|
||||
renderWithTheme(
|
||||
render(
|
||||
<>
|
||||
{getColumnTooltipNode(
|
||||
{
|
||||
@@ -180,7 +175,7 @@ test('should get null as tooltip in metric', () => {
|
||||
|
||||
test('should get metric name, verbose name and description as tooltip in metric', () => {
|
||||
const ref = { current: { scrollWidth: 100, clientWidth: 100 } };
|
||||
renderWithTheme(
|
||||
render(
|
||||
<>
|
||||
{getMetricTooltipNode(
|
||||
{
|
||||
@@ -203,7 +198,7 @@ test('should get metric name, verbose name and description as tooltip in metric'
|
||||
|
||||
test('should get metric name as tooltip if it overflowed', () => {
|
||||
const ref = { current: { scrollWidth: 200, clientWidth: 100 } };
|
||||
renderWithTheme(
|
||||
render(
|
||||
<>
|
||||
{getMetricTooltipNode(
|
||||
{
|
||||
@@ -224,7 +219,7 @@ test('should get metric name as tooltip if it overflowed', () => {
|
||||
|
||||
test('should get metric name, verbose name and description in tooltip if it overflowed', () => {
|
||||
const ref = { current: { scrollWidth: 200, clientWidth: 100 } };
|
||||
renderWithTheme(
|
||||
render(
|
||||
<>
|
||||
{getMetricTooltipNode(
|
||||
{
|
||||
|
||||
68
superset-frontend/packages/superset-ui-core/.eslintrc
Normal file
68
superset-frontend/packages/superset-ui-core/.eslintrc
Normal file
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
{
|
||||
"plugins": ["jest", "jest-dom", "no-only-tests", "testing-library"],
|
||||
"env": {
|
||||
"jest/globals": true
|
||||
},
|
||||
"settings": {
|
||||
"jest": {
|
||||
"version": "detect"
|
||||
}
|
||||
},
|
||||
"extends": [
|
||||
"plugin:jest/recommended",
|
||||
"plugin:jest-dom/recommended",
|
||||
"plugin:testing-library/react"
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"**/*.stories.*",
|
||||
"**/*.overview.*",
|
||||
"**/fixtures.*"
|
||||
],
|
||||
"rules": {
|
||||
"import/no-extraneous-dependencies": "off"
|
||||
}
|
||||
}
|
||||
],
|
||||
"rules": {
|
||||
"import/no-extraneous-dependencies": ["error", { "devDependencies": true }],
|
||||
"jest/consistent-test-it": "error",
|
||||
"no-only-tests/no-only-tests": "error",
|
||||
"prefer-promise-reject-errors": 0,
|
||||
|
||||
"testing-library/no-node-access": "off",
|
||||
"testing-library/prefer-screen-queries": "off",
|
||||
"testing-library/no-container": "off",
|
||||
"testing-library/await-async-queries": "off",
|
||||
"testing-library/await-async-utils": "off",
|
||||
"testing-library/no-await-sync-events": "off",
|
||||
"testing-library/no-render-in-lifecycle": "off",
|
||||
"testing-library/no-unnecessary-act": "off",
|
||||
"testing-library/no-wait-for-multiple-assertions": "off",
|
||||
"testing-library/await-async-events": "off",
|
||||
"testing-library/no-wait-for-side-effects": "off",
|
||||
"testing-library/prefer-presence-queries": "off",
|
||||
"testing-library/render-result-naming-convention": "off",
|
||||
"testing-library/prefer-find-by": "off",
|
||||
"testing-library/no-manual-cleanup": "off"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
module.exports = {};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user