mirror of
https://github.com/apache/superset.git
synced 2026-06-17 13:39:19 +00:00
Compare commits
223 Commits
fix_exampl
...
ts-migrate
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
297515db62 | ||
|
|
0cecb061f0 | ||
|
|
1f2c20fb5b | ||
|
|
5c4fcdb867 | ||
|
|
3880cd5642 | ||
|
|
062b7937f5 | ||
|
|
6bfbbbf89e | ||
|
|
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 | ||
|
|
f00adac73e | ||
|
|
1c28138938 | ||
|
|
cf68c879e2 | ||
|
|
5b991800c3 | ||
|
|
7472714ce7 | ||
|
|
67aa991099 | ||
|
|
5267ec2028 | ||
|
|
3983ee0c2f | ||
|
|
863a0bea5c | ||
|
|
d635c2a9ab | ||
|
|
2c038f5bd6 | ||
|
|
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 |
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 }} )"
|
||||
|
||||
8
.github/workflows/pre-commit.yml
vendored
8
.github/workflows/pre-commit.yml
vendored
@@ -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: |
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,12 @@
|
||||
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 |
|
||||
|
||||
#### 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.9",
|
||||
"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",
|
||||
@@ -44,12 +44,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.8"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
|
||||
@@ -4179,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.9:
|
||||
version "5.24.9"
|
||||
resolved "https://registry.yarnpkg.com/antd/-/antd-5.24.9.tgz#c5862e02ed770bd95e312961f4f0b7b158a004d9"
|
||||
integrity sha512-liB+Y/JwD5/KSKbK1Z1EVAbWcoWYvWJ1s97AbbT+mOdigpJQuWwH7kG8IXNEljI7onvj0DdD43TXhSRLUu9AMA==
|
||||
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"
|
||||
@@ -4199,7 +4199,7 @@ antd@^5.24.9:
|
||||
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"
|
||||
@@ -4219,7 +4219,7 @@ antd@^5.24.9:
|
||||
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"
|
||||
@@ -4229,7 +4229,7 @@ antd@^5.24.9:
|
||||
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"
|
||||
@@ -6247,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"
|
||||
@@ -8211,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"
|
||||
@@ -10605,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"
|
||||
@@ -10821,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"
|
||||
@@ -10929,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"
|
||||
@@ -13033,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.8:
|
||||
version "5.99.8"
|
||||
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.99.8.tgz#dd31a020b7c092d30c4c6d9a4edb95809e7f5946"
|
||||
integrity sha512-lQ3CPiSTpfOnrEGeXDwoq5hIGzSjmwD72GdfVzF7CQAI7t47rJG9eDWvcEkEn3CUQymAElVvDg3YNTlCYj+qUQ==
|
||||
dependencies:
|
||||
"@types/eslint-scope" "^3.7.7"
|
||||
"@types/estree" "^1.0.6"
|
||||
|
||||
@@ -44,7 +44,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",
|
||||
@@ -240,6 +240,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 +278,6 @@ exclude = [
|
||||
"venv",
|
||||
]
|
||||
|
||||
|
||||
# Same as Black.
|
||||
line-length = 88
|
||||
indent-width = 4
|
||||
@@ -367,6 +372,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 +386,7 @@ authorized_licenses = [
|
||||
"osi approved",
|
||||
"psf-2.0",
|
||||
"python software foundation",
|
||||
"simplified bsd",
|
||||
"the unlicense (unlicense)",
|
||||
"the unlicense",
|
||||
]
|
||||
|
||||
@@ -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,7 +8,7 @@ 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
|
||||
@@ -32,7 +32,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 +42,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 +54,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,7 +99,7 @@ email-validator==2.2.0
|
||||
# via flask-appbuilder
|
||||
et-xmlfile==2.0.0
|
||||
# via openpyxl
|
||||
exceptiongroup==1.2.2
|
||||
exceptiongroup==1.3.0
|
||||
# via
|
||||
# cattrs
|
||||
# trio
|
||||
@@ -118,7 +118,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 +152,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 +173,7 @@ idna==3.10
|
||||
# email-validator
|
||||
# requests
|
||||
# trio
|
||||
# url-normalize
|
||||
importlib-metadata==8.7.0
|
||||
# via apache-superset (pyproject.toml)
|
||||
isodate==0.7.2
|
||||
@@ -190,7 +190,7 @@ jsonpath-ng==1.7.0
|
||||
# via apache-superset (pyproject.toml)
|
||||
jsonschema==4.23.0
|
||||
# via flask-appbuilder
|
||||
jsonschema-specifications==2024.10.1
|
||||
jsonschema-specifications==2025.4.1
|
||||
# via jsonschema
|
||||
kombu==5.5.3
|
||||
# via celery
|
||||
@@ -243,7 +243,9 @@ openpyxl==3.1.5
|
||||
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 +265,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 +281,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
|
||||
@@ -336,13 +338,13 @@ requests-cache==1.2.1
|
||||
# via shillelagh
|
||||
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,7 +354,6 @@ six==1.17.0
|
||||
# via
|
||||
# prison
|
||||
# python-dateutil
|
||||
# url-normalize
|
||||
# wtforms-json
|
||||
slack-sdk==3.35.0
|
||||
# via apache-superset (pyproject.toml)
|
||||
@@ -373,7 +374,7 @@ sqlalchemy-utils==0.38.3
|
||||
# via
|
||||
# apache-superset (pyproject.toml)
|
||||
# flask-appbuilder
|
||||
sqlglot==26.16.4
|
||||
sqlglot==26.17.1
|
||||
# via apache-superset (pyproject.toml)
|
||||
sqlparse==0.5.3
|
||||
# via apache-superset (pyproject.toml)
|
||||
@@ -381,17 +382,18 @@ sshtunnel==0.4.0
|
||||
# via apache-superset (pyproject.toml)
|
||||
tabulate==0.8.10
|
||||
# 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
|
||||
# exceptiongroup
|
||||
# limits
|
||||
# pyopenssl
|
||||
# referencing
|
||||
@@ -402,7 +404,7 @@ 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,7 +14,7 @@ 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
|
||||
@@ -51,7 +51,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 +68,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 +76,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 +88,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,7 +176,7 @@ et-xmlfile==2.0.0
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# openpyxl
|
||||
exceptiongroup==1.2.2
|
||||
exceptiongroup==1.3.0
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# cattrs
|
||||
@@ -202,7 +202,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 +280,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 +318,6 @@ greenlet==3.1.1
|
||||
# apache-superset
|
||||
# gevent
|
||||
# shillelagh
|
||||
# sqlalchemy
|
||||
grpcio==1.71.0
|
||||
# via
|
||||
# apache-superset
|
||||
@@ -355,6 +354,7 @@ idna==3.10
|
||||
# email-validator
|
||||
# requests
|
||||
# trio
|
||||
# url-normalize
|
||||
importlib-metadata==8.7.0
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
@@ -389,7 +389,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
|
||||
@@ -495,6 +495,7 @@ outcome==1.3.0.post0
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
# trio
|
||||
# trio-websocket
|
||||
packaging==25.0
|
||||
# via
|
||||
# -c requirements/base.txt
|
||||
@@ -542,7 +543,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 +599,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
|
||||
@@ -731,22 +732,22 @@ 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 +768,6 @@ six==1.17.0
|
||||
# prison
|
||||
# python-dateutil
|
||||
# rfc3339-validator
|
||||
# url-normalize
|
||||
# wtforms-json
|
||||
slack-sdk==3.35.0
|
||||
# via
|
||||
@@ -799,7 +799,7 @@ 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
|
||||
@@ -829,21 +829,22 @@ tqdm==4.67.1
|
||||
# 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
|
||||
# exceptiongroup
|
||||
# limits
|
||||
# pyopenssl
|
||||
# referencing
|
||||
@@ -857,7 +858,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
|
||||
|
||||
@@ -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,
|
||||
@@ -239,6 +244,14 @@ module.exports = {
|
||||
'ImportNamespaceSpecifier[parent.source.value!=/^(\\.|src)/]',
|
||||
message: 'Wildcard imports are not allowed',
|
||||
},
|
||||
{
|
||||
selector: "TSTypeReference[typeName.name='$TSFixMe']",
|
||||
message: 'Fix this $TSFixMe type',
|
||||
},
|
||||
{
|
||||
selector: "TSTypeReference[typeName.name='$TSFixMeFunction']",
|
||||
message: 'Fix this $TSFixMeFunction type',
|
||||
},
|
||||
],
|
||||
'no-restricted-imports': [
|
||||
'error',
|
||||
@@ -295,9 +308,9 @@ module.exports = {
|
||||
'error',
|
||||
{
|
||||
paths: Object.values(restrictedImportsRules).filter(
|
||||
r => r.name !== 'antd-v5',
|
||||
r => r.name !== 'antd',
|
||||
),
|
||||
patterns: ['antd/*'],
|
||||
patterns: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -330,7 +343,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 +388,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');
|
||||
});
|
||||
@@ -254,6 +260,8 @@ describe('Visualization > Table', () => {
|
||||
});
|
||||
|
||||
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'],
|
||||
@@ -261,7 +269,7 @@ describe('Visualization > Table', () => {
|
||||
});
|
||||
|
||||
// Enable server pagination
|
||||
cy.get('[data-test="server_pagination-header"] div.pull-left').click();
|
||||
cy.get(serverPaginationSelector).click();
|
||||
|
||||
// Click row limit control and select high value (200k)
|
||||
cy.get('div[aria-label="Row limit"]').click();
|
||||
@@ -275,7 +283,7 @@ describe('Visualization > Table', () => {
|
||||
cy.get('[data-test="error-tooltip"]').should('not.exist');
|
||||
|
||||
// Disable server pagination
|
||||
cy.get('[data-test="server_pagination-header"] div.pull-left').click();
|
||||
cy.get(serverPaginationSelector).click();
|
||||
|
||||
// Verify error tooltip appears
|
||||
cy.get('[data-test="error-tooltip"]').should('be.visible');
|
||||
@@ -284,17 +292,17 @@ describe('Visualization > Table', () => {
|
||||
cy.get('[data-test="error-tooltip"]').trigger('mouseover');
|
||||
|
||||
// Verify tooltip content
|
||||
cy.get('.antd5-tooltip-inner').should('be.visible');
|
||||
cy.get('.antd5-tooltip-inner').should(
|
||||
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('.antd5-tooltip').invoke('attr', 'style', 'display: none');
|
||||
cy.get('.ant-tooltip').invoke('attr', 'style', 'display: none');
|
||||
|
||||
// Enable server pagination again
|
||||
cy.get('[data-test="server_pagination-header"] div.pull-left').click();
|
||||
cy.get(serverPaginationSelector).click();
|
||||
|
||||
cy.get('[data-test="error-tooltip"]').should('not.exist');
|
||||
|
||||
@@ -319,11 +327,11 @@ describe('Visualization > Table', () => {
|
||||
.trigger('mouseover');
|
||||
|
||||
// Wait for tooltip content and verify
|
||||
cy.get('.antd5-tooltip-inner').should('exist');
|
||||
cy.get('.antd5-tooltip-inner').should('be.visible');
|
||||
cy.get('.ant-tooltip-inner').should('exist');
|
||||
cy.get('.ant-tooltip-inner').should('be.visible');
|
||||
|
||||
// Verify tooltip content separately
|
||||
cy.get('.antd5-tooltip-inner').should('contain', 'Value cannot exceed');
|
||||
cy.get('.ant-tooltip-inner').should('contain', 'Value cannot exceed');
|
||||
});
|
||||
|
||||
it('Test sorting with server pagination enabled', () => {
|
||||
@@ -393,12 +401,12 @@ describe('Visualization > Table', () => {
|
||||
|
||||
cy.wait('@chartData');
|
||||
|
||||
// Basic search test
|
||||
cy.get('span.dt-global-filter input.form-control.input-sm').should(
|
||||
'be.visible',
|
||||
);
|
||||
const searchInputSelector = '.dt-global-filter input';
|
||||
|
||||
cy.get('span.dt-global-filter input.form-control.input-sm').type('John');
|
||||
// Basic search test
|
||||
cy.get(searchInputSelector).should('be.visible');
|
||||
|
||||
cy.get(searchInputSelector).type('John');
|
||||
|
||||
cy.wait('@chartData');
|
||||
|
||||
@@ -407,11 +415,11 @@ describe('Visualization > Table', () => {
|
||||
});
|
||||
|
||||
// Clear and test case-insensitive search
|
||||
cy.get('span.dt-global-filter input.form-control.input-sm').clear();
|
||||
cy.get(searchInputSelector).clear();
|
||||
|
||||
cy.wait('@chartData');
|
||||
|
||||
cy.get('span.dt-global-filter input.form-control.input-sm').type('mary');
|
||||
cy.get(searchInputSelector).type('mary');
|
||||
|
||||
cy.wait('@chartData');
|
||||
|
||||
@@ -420,9 +428,9 @@ describe('Visualization > Table', () => {
|
||||
});
|
||||
|
||||
// Test special characters
|
||||
cy.get('span.dt-global-filter input.form-control.input-sm').clear();
|
||||
cy.get(searchInputSelector).clear();
|
||||
|
||||
cy.get('span.dt-global-filter input.form-control.input-sm').type('Nicole');
|
||||
cy.get(searchInputSelector).type('Nicole');
|
||||
|
||||
cy.wait('@chartData');
|
||||
|
||||
@@ -431,9 +439,9 @@ describe('Visualization > Table', () => {
|
||||
});
|
||||
|
||||
// Test no results
|
||||
cy.get('span.dt-global-filter input.form-control.input-sm').clear();
|
||||
cy.get(searchInputSelector).clear();
|
||||
|
||||
cy.get('span.dt-global-filter input.form-control.input-sm').type('XYZ123');
|
||||
cy.get(searchInputSelector).type('XYZ123');
|
||||
|
||||
cy.wait('@chartData');
|
||||
|
||||
@@ -450,9 +458,9 @@ describe('Visualization > Table', () => {
|
||||
|
||||
cy.get('.ant-select-item-option').contains('state').click();
|
||||
|
||||
cy.get('span.dt-global-filter input.form-control.input-sm').clear();
|
||||
cy.get(searchInputSelector).clear();
|
||||
|
||||
cy.get('span.dt-global-filter input.form-control.input-sm').type('CA');
|
||||
cy.get(searchInputSelector).type('CA');
|
||||
|
||||
cy.wait('@chartData');
|
||||
cy.wait(1000);
|
||||
|
||||
@@ -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: {
|
||||
|
||||
8323
superset-frontend/package-lock.json
generated
8323
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,7 +70,10 @@
|
||||
"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",
|
||||
"tsc": "tsc ",
|
||||
"ts-migrate-all": "ts-migrate rename . && ts-migrate migrate --aliases tsfixme . && ts-migrate reignore .",
|
||||
"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 +84,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 +115,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 +123,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 +140,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-qgis-parser": "^2.0.0",
|
||||
"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 +171,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,14 +193,12 @@
|
||||
"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",
|
||||
@@ -230,7 +224,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",
|
||||
@@ -262,12 +256,12 @@
|
||||
"@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 +271,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 +279,14 @@
|
||||
"@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",
|
||||
@@ -304,9 +298,12 @@
|
||||
"css-minimizer-webpack-plugin": "^7.0.2",
|
||||
"enzyme": "^3.11.0",
|
||||
"enzyme-matchers": "^7.1.2",
|
||||
"esbuild": "^0.20.0",
|
||||
"esbuild-loader": "^4.2.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 +327,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",
|
||||
@@ -354,7 +351,10 @@
|
||||
"thread-loader": "^4.0.4",
|
||||
"ts-jest": "^29.2.5",
|
||||
"ts-loader": "^9.5.1",
|
||||
"ts-migrate": "^0.1.35",
|
||||
"ts-migrate-plugins": "^0.1.35",
|
||||
"tscw-config": "^1.1.2",
|
||||
"tsx": "^4.19.2",
|
||||
"typescript": "5.1.6",
|
||||
"vm-browserify": "^1.1.2",
|
||||
"webpack": "^5.98.0",
|
||||
@@ -365,6 +365,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 +380,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;
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
|
||||
@@ -40,7 +40,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};
|
||||
`}
|
||||
`;
|
||||
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -300,7 +300,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,
|
||||
@@ -371,7 +371,9 @@ export type CustomControlItem = {
|
||||
export const isCustomControlItem = (obj: unknown): obj is CustomControlItem =>
|
||||
typeof obj === 'object' &&
|
||||
obj !== null &&
|
||||
// @ts-expect-error TS(2339): Property 'name' does not exist on type 'object'.
|
||||
typeof ('name' in obj && obj.name) === 'string' &&
|
||||
// @ts-expect-error TS(2339): Property 'config' does not exist on type 'object'.
|
||||
typeof ('config' in obj && obj.config) === 'object' &&
|
||||
(obj as CustomControlItem).config !== null;
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
* under the License.
|
||||
*/
|
||||
import '@testing-library/jest-dom';
|
||||
import { render } from '@testing-library/react';
|
||||
import { render } from '@superset-ui/core/spec';
|
||||
import {
|
||||
ThemeProvider,
|
||||
supersetTheme,
|
||||
@@ -26,17 +26,21 @@ import {
|
||||
|
||||
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: {
|
||||
@@ -114,11 +118,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 +130,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,11 @@
|
||||
* under the License.
|
||||
*/
|
||||
import '@testing-library/jest-dom';
|
||||
import { fireEvent, render } from '@testing-library/react';
|
||||
import { fireEvent, render } from '@superset-ui/core/spec';
|
||||
import { ThemeProvider, supersetTheme } from '@superset-ui/core';
|
||||
import { InfoTooltipWithTrigger, InfoTooltipWithTriggerProps } from '../../src';
|
||||
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,10 +29,10 @@ jest.mock('../../src/components/Tooltip', () => ({
|
||||
|
||||
const defaultProps = {};
|
||||
|
||||
const setup = (props: Partial<InfoTooltipWithTriggerProps> = {}) =>
|
||||
const setup = (props: Partial<InfoTooltipProps> = {}) =>
|
||||
render(
|
||||
<ThemeProvider theme={supersetTheme}>
|
||||
<InfoTooltipWithTrigger {...defaultProps} {...props} />
|
||||
<InfoTooltip {...defaultProps} {...props} />
|
||||
</ThemeProvider>,
|
||||
);
|
||||
|
||||
@@ -44,31 +44,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 +74,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,31 @@
|
||||
* under the License.
|
||||
*/
|
||||
import '@testing-library/jest-dom';
|
||||
import { render } from '@testing-library/react';
|
||||
import { render } from '@superset-ui/core/spec';
|
||||
import { ThemeProvider, supersetTheme } from '@superset-ui/core';
|
||||
import { MetricOption, MetricOptionProps } from '../../src';
|
||||
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" />,
|
||||
}));
|
||||
|
||||
@@ -65,9 +72,9 @@ test('shows a label with verbose_name', () => {
|
||||
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 +89,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({
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
* 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 {
|
||||
|
||||
@@ -129,7 +129,7 @@ test('returns empty array if timeseries_limit_metric is an empty array', () => {
|
||||
expect(
|
||||
extractExtraMetrics({
|
||||
...baseFormData,
|
||||
// @ts-ignore
|
||||
// @ts-expect-error TS(2322): Type 'never[]' is not assignable to type 'QueryFor... Remove this comment to see the full error message
|
||||
timeseries_limit_metric: [],
|
||||
}),
|
||||
).toEqual([]);
|
||||
|
||||
@@ -51,7 +51,7 @@ describe('defineSavedMetrics', () => {
|
||||
uuid: '1',
|
||||
},
|
||||
]);
|
||||
// @ts-ignore
|
||||
// @ts-expect-error TS(2322): Type 'undefined' is not assignable to type 'Metric... Remove this comment to see the full error message
|
||||
expect(defineSavedMetrics({ ...dataset, metrics: undefined })).toEqual([]);
|
||||
});
|
||||
|
||||
|
||||
@@ -284,7 +284,7 @@ describe('getColorFunction()', () => {
|
||||
it('getColorFunction unsupported operator', () => {
|
||||
const colorFunction = getColorFunction(
|
||||
{
|
||||
// @ts-ignore
|
||||
// @ts-expect-error TS(2322): Type '"unsupported operator"' is not assignable to... Remove this comment to see the full error message
|
||||
operator: 'unsupported operator',
|
||||
targetValue: 50,
|
||||
colorScheme: '#FF0000',
|
||||
|
||||
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,29 @@
|
||||
/**
|
||||
* 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 { SVGProps, forwardRef } from 'react';
|
||||
|
||||
const SvgrMock = forwardRef<SVGSVGElement, SVGProps<SVGSVGElement>>(
|
||||
(props, ref) => <svg ref={ref} {...props} />,
|
||||
);
|
||||
|
||||
SvgrMock.displayName = 'SvgrMock';
|
||||
|
||||
export const ReactComponent = SvgrMock;
|
||||
export default SvgrMock;
|
||||
@@ -24,21 +24,37 @@
|
||||
"lib"
|
||||
],
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^5.2.6",
|
||||
"@babel/runtime": "^7.25.6",
|
||||
"@fontsource/fira-code": "^5.0.18",
|
||||
"@fontsource/inter": "^5.0.20",
|
||||
"@types/json-bigint": "^1.0.4",
|
||||
"ace-builds": "^1.41.0",
|
||||
"brace": "^0.11.1",
|
||||
"classnames": "^2.2.5",
|
||||
"csstype": "^3.1.3",
|
||||
"core-js": "^3.38.1",
|
||||
"d3-format": "^1.3.2",
|
||||
"dayjs": "^1.11.13",
|
||||
"d3-interpolate": "^3.0.1",
|
||||
"d3-scale": "^3.0.0",
|
||||
"d3-time": "^3.1.0",
|
||||
"d3-time-format": "^4.1.0",
|
||||
"dompurify": "^3.2.4",
|
||||
"fetch-retry": "^6.0.0",
|
||||
"jed": "^1.1.1",
|
||||
"lodash": "^4.17.21",
|
||||
"math-expression-evaluator": "^2.0.6",
|
||||
"pretty-ms": "^9.2.0",
|
||||
"re-resizable": "^6.10.1",
|
||||
"react-ace": "^10.1.0",
|
||||
"react-js-cron": "^2.1.2",
|
||||
"react-draggable": "^4.4.6",
|
||||
"react-resize-detector": "^7.1.2",
|
||||
"react-ultimate-pagination": "^1.3.2",
|
||||
"react-error-boundary": "^5.0.0",
|
||||
"react-markdown": "^8.0.7",
|
||||
"regenerator-runtime": "^0.14.1",
|
||||
"rehype-raw": "^7.0.0",
|
||||
"rehype-sanitize": "^6.0.0",
|
||||
"remark-gfm": "^3.0.1",
|
||||
@@ -56,8 +72,10 @@
|
||||
"@types/d3-scale": "^2.1.1",
|
||||
"@types/d3-time": "^3.0.4",
|
||||
"@types/d3-time-format": "^4.0.3",
|
||||
"@types/react-table": "^7.7.20",
|
||||
"@types/enzyme": "^3.10.18",
|
||||
"@types/fetch-mock": "^7.3.8",
|
||||
"@types/jquery": "^3.5.8",
|
||||
"@types/lodash": "^4.17.16",
|
||||
"@types/math-expression-evaluator": "^1.3.3",
|
||||
"@types/node": "^22.10.3",
|
||||
@@ -70,6 +88,7 @@
|
||||
"timezone-mock": "1.3.6"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"antd": "^5.24.6",
|
||||
"@emotion/cache": "^11.4.0",
|
||||
"@emotion/react": "^11.4.1",
|
||||
"@emotion/styled": "^11.3.0",
|
||||
@@ -80,6 +99,7 @@
|
||||
"@testing-library/user-event": "*",
|
||||
"@types/react": "*",
|
||||
"@types/react-loadable": "*",
|
||||
"@types/react-window": "^1.8.8",
|
||||
"@types/tinycolor2": "*",
|
||||
"nanoid": "^5.0.9",
|
||||
"react": "^17.0.2",
|
||||
@@ -88,5 +108,27 @@
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./esm/index.js",
|
||||
"require": "./lib/index.js",
|
||||
"types": "./lib/index.d.ts"
|
||||
},
|
||||
"./components/*": {
|
||||
"import": "./esm/components/*/index.js",
|
||||
"require": "./lib/components/*/index.js",
|
||||
"types": "./lib/components/*/index.d.ts"
|
||||
},
|
||||
"./components": {
|
||||
"import": "./esm/components/index.js",
|
||||
"require": "./lib/components/index.js",
|
||||
"types": "./lib/components/index.d.ts"
|
||||
},
|
||||
"./utils/*": {
|
||||
"import": "./esm/utils/*.js",
|
||||
"require": "./lib/utils/*.js",
|
||||
"types": "./lib/utils/*.d.ts"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { CSSProperties, PureComponent, ReactNode } from 'react';
|
||||
import { CSSProperties, ReactNode } from 'react';
|
||||
import { Table, type TableColumnsType } from 'antd';
|
||||
|
||||
interface TooltipRowData {
|
||||
key: string | number;
|
||||
@@ -27,43 +27,52 @@ interface TooltipRowData {
|
||||
valueStyle?: CSSProperties;
|
||||
}
|
||||
|
||||
const defaultProps = {
|
||||
className: '',
|
||||
data: [] as TooltipRowData[],
|
||||
};
|
||||
|
||||
type Props = {
|
||||
interface TooltipTableProps {
|
||||
className?: string;
|
||||
data: TooltipRowData[];
|
||||
} & Readonly<typeof defaultProps>;
|
||||
}
|
||||
|
||||
const VALUE_CELL_STYLE: CSSProperties = { paddingLeft: 8, textAlign: 'right' };
|
||||
|
||||
export default class TooltipTable extends PureComponent<Props, {}> {
|
||||
static defaultProps = defaultProps;
|
||||
const TooltipTable = ({ className = '', data }: TooltipTableProps) => {
|
||||
const columns: TableColumnsType<TooltipRowData> = [
|
||||
{
|
||||
title: '',
|
||||
dataIndex: 'keyColumn',
|
||||
key: 'keyColumn',
|
||||
render: (text, record) => (
|
||||
<div style={record.keyStyle}>{record.keyColumn ?? record.key}</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
dataIndex: 'valueColumn',
|
||||
key: 'valueColumn',
|
||||
align: 'right',
|
||||
render: (text, record) => (
|
||||
<div
|
||||
style={
|
||||
record.valueStyle
|
||||
? { ...VALUE_CELL_STYLE, ...record.valueStyle }
|
||||
: VALUE_CELL_STYLE
|
||||
}
|
||||
>
|
||||
{record.valueColumn}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
render() {
|
||||
const { className, data } = this.props;
|
||||
return (
|
||||
<Table
|
||||
className={className}
|
||||
columns={columns}
|
||||
dataSource={data}
|
||||
pagination={false}
|
||||
showHeader={false}
|
||||
bordered={false}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<table className={className}>
|
||||
<tbody>
|
||||
{data.map(({ key, keyColumn, keyStyle, valueColumn, valueStyle }) => (
|
||||
<tr key={key}>
|
||||
<td style={keyStyle}>{keyColumn ?? key}</td>
|
||||
<td
|
||||
style={
|
||||
valueStyle
|
||||
? { ...VALUE_CELL_STYLE, ...valueStyle }
|
||||
: VALUE_CELL_STYLE
|
||||
}
|
||||
>
|
||||
{valueColumn}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
}
|
||||
export default TooltipTable;
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
|
||||
import { t } from '@superset-ui/core';
|
||||
import { SupersetTheme } from '../../style';
|
||||
import { SupersetTheme } from '../..';
|
||||
import { FallbackPropsWithDimension } from './SuperChart';
|
||||
|
||||
export type Props = FallbackPropsWithDimension;
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
|
||||
import { CSSProperties } from 'react';
|
||||
import { css, styled } from '../../style';
|
||||
import { css, styled } from '../../theme';
|
||||
import { t } from '../../translation';
|
||||
|
||||
const MESSAGE_STYLES: CSSProperties = { maxWidth: 800 };
|
||||
@@ -36,16 +36,16 @@ const Container = styled.div<{
|
||||
text-align: center;
|
||||
height: ${height}px;
|
||||
width: ${width}px;
|
||||
padding: ${theme.gridUnit * 4}px;
|
||||
padding: ${theme.sizeUnit * 4}px;
|
||||
|
||||
& .no-results-title {
|
||||
font-size: ${theme.typography.sizes.l}px;
|
||||
font-weight: ${theme.typography.weights.bold};
|
||||
padding-bottom: ${theme.gridUnit * 2};
|
||||
font-size: ${theme.fontSizeLG}px;
|
||||
font-weight: ${theme.fontWeightStrong};
|
||||
padding-bottom: ${theme.sizeUnit * 2};
|
||||
}
|
||||
|
||||
& .no-results-body {
|
||||
font-size: ${theme.typography.sizes.m}px;
|
||||
font-size: ${theme.fontSize}px;
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
|
||||
// eslint-disable-next-line no-restricted-syntax -- whole React import is required for `reactify.test.tsx` Jest test passing.
|
||||
import React, { Component, ComponentClass, WeakValidationMap } from 'react';
|
||||
import { Component, ComponentClass, WeakValidationMap } from 'react';
|
||||
|
||||
// TODO: Note that id and className can collide between Props and ReactifyProps
|
||||
// leading to (likely) unexpected behaviors. We should either require Props to not
|
||||
|
||||
@@ -49,6 +49,7 @@ export interface ChartMetadataConfig {
|
||||
label?: ChartLabel | null;
|
||||
labelExplanation?: string | null;
|
||||
queryObjectCount?: number;
|
||||
dynamicQueryObjectCount?: boolean;
|
||||
parseMethod?: ParseMethod;
|
||||
// suppressContextMenu: true hides the default context menu for the chart.
|
||||
// This is useful for viz plugins that define their own context menu.
|
||||
@@ -92,6 +93,8 @@ export default class ChartMetadata {
|
||||
|
||||
queryObjectCount: number;
|
||||
|
||||
dynamicQueryObjectCount: boolean;
|
||||
|
||||
parseMethod: ParseMethod;
|
||||
|
||||
suppressContextMenu?: boolean;
|
||||
@@ -115,6 +118,7 @@ export default class ChartMetadata {
|
||||
label = null,
|
||||
labelExplanation = null,
|
||||
queryObjectCount = 1,
|
||||
dynamicQueryObjectCount = false,
|
||||
parseMethod = 'json-bigint',
|
||||
suppressContextMenu = false,
|
||||
} = config;
|
||||
@@ -145,6 +149,7 @@ export default class ChartMetadata {
|
||||
this.label = label;
|
||||
this.labelExplanation = labelExplanation;
|
||||
this.queryObjectCount = queryObjectCount;
|
||||
this.dynamicQueryObjectCount = dynamicQueryObjectCount;
|
||||
this.parseMethod = parseMethod;
|
||||
this.suppressContextMenu = suppressContextMenu;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ import {
|
||||
SetDataMaskHook,
|
||||
} from '../types/Base';
|
||||
import { QueryData, DataRecordFilters } from '..';
|
||||
import { SupersetTheme } from '../../style';
|
||||
import { SupersetTheme } from '../../theme';
|
||||
|
||||
// TODO: more specific typing for these fields of ChartProps
|
||||
type AnnotationData = PlainObject;
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import Alert, { AlertProps } from './index';
|
||||
import { Alert } from '.';
|
||||
import type { AlertProps } from './types';
|
||||
|
||||
type AlertType = Required<Pick<AlertProps, 'type'>>;
|
||||
type AlertTypeValue = AlertType['type'];
|
||||
@@ -30,7 +31,7 @@ const bigText =
|
||||
'purus convallis placerat in at nunc. Nulla nec viverra augue.';
|
||||
|
||||
export default {
|
||||
title: 'Alert',
|
||||
title: 'Components/Alert',
|
||||
component: Alert,
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user