Compare commits

..

1 Commits

Author SHA1 Message Date
Maxime Beauchemin
c778c15259 docs(ci): auto-regenerate openapi files used for docs 2025-05-06 16:02:41 -07:00
1768 changed files with 30165 additions and 48666 deletions

1
.gitattributes vendored
View File

@@ -1,4 +1,3 @@
docker/**/*.sh text eol=lf
*.svg binary
*.ipynb binary
*.geojson binary

View File

@@ -27,8 +27,6 @@ updates:
- package-ecosystem: "uv"
directory: "requirements/"
open-pull-requests-limit: 10
schedule:
interval: "weekly"
labels:
- uv
- dependabot

View File

@@ -48,8 +48,6 @@ 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"

View File

@@ -111,9 +111,6 @@ 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 }} )"

View File

@@ -53,14 +53,6 @@ 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

View File

@@ -41,6 +41,8 @@ jobs:
node-version-file: './docs/.nvmrc'
- name: Setup Python
uses: ./.github/actions/setup-backend/
- name: Update openapi docs
run: superset update_api_docs
- uses: actions/setup-java@v4
with:
distribution: 'zulu'

View File

@@ -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 version first!
# an ASF Infra ticket to allow the new verison first!
- uses: JustinBeckwith/linkinator-action@v1.11.0
continue-on-error: true # This will make the job advisory (non-blocking, no red X)
with:

View File

@@ -73,7 +73,6 @@ 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
@@ -138,16 +137,9 @@ 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 }}--${{ steps.set-safe-app-root.outputs.safe_app_root }}
name: cypress-artifact-${{ github.run_id }}-${{ github.job }}-${{ matrix.browser }}-${{ matrix.parallel_id }}

View File

@@ -26,8 +26,6 @@ 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
@@ -41,10 +39,6 @@ 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 \
@@ -121,6 +115,24 @@ 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'
@@ -132,8 +144,7 @@ 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: |

View File

@@ -58,7 +58,7 @@ repos:
- id: prettier
additional_dependencies:
- prettier@3.5.3
args: ["--ignore-path=./superset-frontend/.prettierignore", "--exclude", "site-packages"]
args: ["--ignore-path=./superset-frontend/.prettierignore"]
files: "superset-frontend"
- repo: local
hooks:

View File

@@ -18,7 +18,7 @@
######################################################################
# Node stage to deal with static asset construction
######################################################################
ARG PY_VER=3.11.12-slim-bookworm
ARG PY_VER=3.11.11-slim-bookworm
# If BUILDPLATFORM is null, set it to 'amd64' (or leave as is otherwise).
ARG BUILDPLATFORM=${BUILDPLATFORM:-amd64}

View File

@@ -43,7 +43,6 @@ 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]

View File

@@ -26,7 +26,6 @@ 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.

View File

@@ -302,15 +302,6 @@ AUTH_USER_REGISTRATION = True
AUTH_USER_REGISTRATION_ROLE = "Public"
```
In case you want to assign the `Admin` role on new user registration, it can be assigned as follows:
```python
AUTH_USER_REGISTRATION_ROLE = "Admin"
```
If you encounter the [issue](https://github.com/apache/superset/issues/13243) of not being able to list users from the Superset main page settings, although a newly registered user has an `Admin` role, please re-run `superset init` to sync the required permissions. Below is the command to re-run `superset init` using docker compose.
```
docker-compose exec superset superset init
```
Then, create a `CustomSsoSecurityManager` that extends `SupersetSecurityManager` and overrides
`oauth_user_info`:

View File

@@ -250,14 +250,6 @@ 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

View File

@@ -64,56 +64,6 @@ 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

View File

@@ -27,7 +27,9 @@ 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 [Building your own production Docker image](/docs/installation/docker-builds/#building-your-own-production-docker-image).
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.
## [Kubernetes (K8s)](/docs/installation/kubernetes.mdx)

View File

@@ -2,12 +2,6 @@
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 |

View File

@@ -26,10 +26,10 @@
"@emotion/styled": "^10.0.27",
"@saucelabs/theme-github-codeblock": "^0.3.0",
"@superset-ui/style": "^0.14.23",
"antd": "^5.25.1",
"antd": "^5.24.5",
"docusaurus-plugin-less": "^2.0.2",
"less": "^4.3.0",
"less-loader": "^12.3.0",
"less-loader": "^11.0.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.5",
"eslint-config-prettier": "^10.1.2",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-react": "^7.37.5",
"prettier": "^2.0.0",
"typescript": "~5.8.3",
"webpack": "^5.99.8"
"webpack": "^5.99.7"
},
"browserslist": {
"production": [

View File

@@ -1092,13 +1092,20 @@
core-js-pure "^3.30.2"
regenerator-runtime "^0.14.0"
"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.3", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.7", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.6", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.4", "@babel/runtime@^7.24.7", "@babel/runtime@^7.24.8", "@babel/runtime@^7.25.7", "@babel/runtime@^7.25.9", "@babel/runtime@^7.26.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4":
"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.25.9", "@babel/runtime@^7.8.4":
version "7.27.0"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.27.0.tgz#fbee7cf97c709518ecc1f590984481d5460d4762"
integrity sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==
dependencies:
regenerator-runtime "^0.14.0"
"@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.16.7", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.6", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.4", "@babel/runtime@^7.24.7", "@babel/runtime@^7.24.8", "@babel/runtime@^7.25.7", "@babel/runtime@^7.26.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2":
version "7.26.10"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.10.tgz#a07b4d8fa27af131a633d7b3524db803eb4764c2"
integrity sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==
dependencies:
regenerator-runtime "^0.14.0"
"@babel/template@^7.25.9", "@babel/template@^7.26.9", "@babel/template@^7.27.0":
version "7.27.0"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.0.tgz#b253e5406cc1df1c57dcd18f11760c2dbf40c0b4"
@@ -4179,10 +4186,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.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==
antd@^5.24.5:
version "5.24.5"
resolved "https://registry.yarnpkg.com/antd/-/antd-5.24.5.tgz#b0976a113163888d1477f9e666c3c23352b098e9"
integrity sha512-1lAv/G+9ewQanyoAo3JumQmIlVxwo5QwWGb6QCHYc40Cq0NxC/EzITcjsgq1PSaTUpLkKq8A2l7Fjtu47vqQBg==
dependencies:
"@ant-design/colors" "^7.2.0"
"@ant-design/cssinjs" "^1.23.0"
@@ -4199,37 +4206,37 @@ antd@^5.25.1:
classnames "^2.5.1"
copy-to-clipboard "^3.3.3"
dayjs "^1.11.11"
rc-cascader "~3.34.0"
rc-cascader "~3.33.1"
rc-checkbox "~3.5.0"
rc-collapse "~3.9.0"
rc-dialog "~9.6.0"
rc-drawer "~7.2.0"
rc-dropdown "~4.2.1"
rc-field-form "~2.7.0"
rc-image "~7.12.0"
rc-input "~1.8.0"
rc-input-number "~9.5.0"
rc-mentions "~2.20.0"
rc-image "~7.11.1"
rc-input "~1.7.3"
rc-input-number "~9.4.0"
rc-mentions "~2.19.1"
rc-menu "~9.16.1"
rc-motion "^2.9.5"
rc-notification "~5.6.4"
rc-notification "~5.6.3"
rc-pagination "~5.1.0"
rc-picker "~4.11.3"
rc-progress "~4.0.0"
rc-rate "~2.13.1"
rc-resize-observer "^1.4.3"
rc-segmented "~2.7.0"
rc-select "~14.16.7"
rc-select "~14.16.6"
rc-slider "~11.1.8"
rc-steps "~6.0.1"
rc-switch "~4.1.0"
rc-table "~7.50.4"
rc-tabs "~15.6.1"
rc-textarea "~1.10.0"
rc-tabs "~15.5.1"
rc-textarea "~1.9.0"
rc-tooltip "~6.4.0"
rc-tree "~5.13.1"
rc-tree-select "~5.27.0"
rc-upload "~4.9.0"
rc-upload "~4.8.1"
rc-util "^5.44.4"
scroll-into-view-if-needed "^3.1.0"
throttle-debounce "^5.0.2"
@@ -5667,7 +5674,12 @@ data-view-byte-offset@^1.0.1:
es-errors "^1.3.0"
is-data-view "^1.0.1"
dayjs@^1.11.11, dayjs@^1.11.13:
dayjs@^1.11.11:
version "1.11.12"
resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.12.tgz"
integrity sha512-Rt2g+nTbLlDWZTwwrIXjy9MeiZmSDI375FvZs72ngxx8PDC6YXOeR3q5LAuPzjZQxhiWdRKac7RKV+YyQYfYIg==
dayjs@^1.11.13:
version "1.11.13"
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.13.tgz#92430b0139055c3ebb60150aa13e860a4b5a366c"
integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==
@@ -6247,10 +6259,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.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-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-plugin-prettier@^4.0.0:
version "4.2.1"
@@ -8211,10 +8223,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@^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-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@^4.3.0:
version "4.3.0"
@@ -10605,10 +10617,10 @@ raw-body@2.5.2:
iconv-lite "0.4.24"
unpipe "1.0.0"
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==
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==
dependencies:
"@babel/runtime" "^7.25.7"
classnames "^2.3.1"
@@ -10676,10 +10688,10 @@ rc-field-form@~2.7.0:
"@rc-component/async-validator" "^5.0.3"
rc-util "^5.32.2"
rc-image@~7.12.0:
version "7.12.0"
resolved "https://registry.yarnpkg.com/rc-image/-/rc-image-7.12.0.tgz#95e9314701e668217d113c1f29b4f01ac025cafe"
integrity sha512-cZ3HTyyckPnNnUb9/DRqduqzLfrQRyi+CdHjdqgsyDpI3Ln5UX1kXnAhPBSJj9pVRzwRFgqkN7p9b6HBDjmu/Q==
rc-image@~7.11.1:
version "7.11.1"
resolved "https://registry.yarnpkg.com/rc-image/-/rc-image-7.11.1.tgz#3ab290708dc053d3681de94186522e4e594f6772"
integrity sha512-XuoWx4KUXg7hNy5mRTy1i8c8p3K8boWg6UajbHpDXS5AlRVucNfTi5YxTtPBTBzegxAZpvuLfh3emXFt6ybUdA==
dependencies:
"@babel/runtime" "^7.11.2"
"@rc-component/portal" "^1.0.2"
@@ -10688,37 +10700,37 @@ rc-image@~7.12.0:
rc-motion "^2.6.2"
rc-util "^5.34.1"
rc-input-number@~9.5.0:
version "9.5.0"
resolved "https://registry.yarnpkg.com/rc-input-number/-/rc-input-number-9.5.0.tgz#b47963d0f2cbd85ab2f1badfdc089a904c073f38"
integrity sha512-bKaEvB5tHebUURAEXw35LDcnRZLq3x1k7GxfAqBMzmpHkDGzjAtnUL8y4y5N15rIFIg5IJgwr211jInl3cipag==
rc-input-number@~9.4.0:
version "9.4.0"
resolved "https://registry.npmjs.org/rc-input-number/-/rc-input-number-9.4.0.tgz"
integrity sha512-Tiy4DcXcFXAf9wDhN8aUAyMeCLHJUHA/VA/t7Hj8ZEx5ETvxG7MArDOSE6psbiSCo+vJPm4E3fGN710ITVn6GA==
dependencies:
"@babel/runtime" "^7.10.1"
"@rc-component/mini-decimal" "^1.0.1"
classnames "^2.2.5"
rc-input "~1.8.0"
rc-input "~1.7.1"
rc-util "^5.40.1"
rc-input@~1.8.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/rc-input/-/rc-input-1.8.0.tgz#d2f4404befebf2fbdc28390d5494c302f74ae974"
integrity sha512-KXvaTbX+7ha8a/k+eg6SYRVERK0NddX8QX7a7AnRvUa/rEH0CNMlpcBzBkhI0wp2C8C4HlMoYl8TImSN+fuHKA==
rc-input@~1.7.1, rc-input@~1.7.3:
version "1.7.3"
resolved "https://registry.yarnpkg.com/rc-input/-/rc-input-1.7.3.tgz#cb334a17b93ce985bceb243b4c111a5ed641e0e3"
integrity sha512-A5w4egJq8+4JzlQ55FfQjDnPvOaAbzwC3VLOAdOytyek3TboSOP9qxN+Gifup+shVXfvecBLBbWBpWxmk02SWQ==
dependencies:
"@babel/runtime" "^7.11.1"
classnames "^2.2.1"
rc-util "^5.18.1"
rc-mentions@~2.20.0:
version "2.20.0"
resolved "https://registry.yarnpkg.com/rc-mentions/-/rc-mentions-2.20.0.tgz#3bbeac0352b02e0ce3e1244adb48701bb6903bf7"
integrity sha512-w8HCMZEh3f0nR8ZEd466ATqmXFCMGMN5UFCzEUL0bM/nGw/wOS2GgRzKBcm19K++jDyuWCOJOdgcKGXU3fXfbQ==
rc-mentions@~2.19.1:
version "2.19.1"
resolved "https://registry.npmjs.org/rc-mentions/-/rc-mentions-2.19.1.tgz"
integrity sha512-KK3bAc/bPFI993J3necmaMXD2reZTzytZdlTvkeBbp50IGH1BDPDvxLdHDUrpQx2b2TGaVJsn+86BvYa03kGqA==
dependencies:
"@babel/runtime" "^7.22.5"
"@rc-component/trigger" "^2.0.0"
classnames "^2.2.6"
rc-input "~1.8.0"
rc-input "~1.7.1"
rc-menu "~9.16.0"
rc-textarea "~1.10.0"
rc-textarea "~1.9.0"
rc-util "^5.34.1"
rc-menu@~9.16.0, rc-menu@~9.16.1:
@@ -10742,10 +10754,10 @@ rc-motion@^2.0.0, rc-motion@^2.0.1, rc-motion@^2.3.0, rc-motion@^2.3.4, rc-motio
classnames "^2.2.1"
rc-util "^5.44.0"
rc-notification@~5.6.4:
version "5.6.4"
resolved "https://registry.yarnpkg.com/rc-notification/-/rc-notification-5.6.4.tgz#ea89c39c13cd517fdfd97fe63f03376fabb78544"
integrity sha512-KcS4O6B4qzM3KH7lkwOB7ooLPZ4b6J+VMmQgT51VZCeEcmghdeR4IrMcFq0LG+RPdnbe/ArT086tGM8Snimgiw==
rc-notification@~5.6.3:
version "5.6.3"
resolved "https://registry.npmjs.org/rc-notification/-/rc-notification-5.6.3.tgz"
integrity sha512-42szwnn8VYQoT6GnjO00i1iwqV9D1TTMvxObWsuLwgl0TsOokzhkYiufdtQBsJMFjJravS1hfDKVMHLKLcPE4g==
dependencies:
"@babel/runtime" "^7.10.1"
classnames "2.x"
@@ -10821,10 +10833,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.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==
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==
dependencies:
"@babel/runtime" "^7.10.1"
"@rc-component/trigger" "^2.1.1"
@@ -10873,10 +10885,10 @@ rc-table@~7.50.4:
rc-util "^5.44.3"
rc-virtual-list "^3.14.2"
rc-tabs@~15.6.1:
version "15.6.1"
resolved "https://registry.yarnpkg.com/rc-tabs/-/rc-tabs-15.6.1.tgz#f0b6c65384dfa09a64eb539e86a0667c7a650708"
integrity sha512-/HzDV1VqOsUWyuC0c6AkxVYFjvx9+rFPKZ32ejxX0Uc7QCzcEjTA9/xMgv4HemPKwzBNX8KhGVbbumDjnj92aA==
rc-tabs@~15.5.1:
version "15.5.1"
resolved "https://registry.npmjs.org/rc-tabs/-/rc-tabs-15.5.1.tgz"
integrity sha512-yiWivLAjEo5d1v2xlseB2dQocsOhkoVSfo1krS8v8r+02K+TBUjSjXIf7dgyVSxp6wRIPv5pMi5hanNUlQMgUA==
dependencies:
"@babel/runtime" "^7.11.2"
classnames "2.x"
@@ -10886,14 +10898,14 @@ rc-tabs@~15.6.1:
rc-resize-observer "^1.0.0"
rc-util "^5.34.1"
rc-textarea@~1.10.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/rc-textarea/-/rc-textarea-1.10.0.tgz#f8f962ef83be0b8e35db97cf03dbfb86ddd9c46c"
integrity sha512-ai9IkanNuyBS4x6sOL8qu/Ld40e6cEs6pgk93R+XLYg0mDSjNBGey6/ZpDs5+gNLD7urQ14po3V6Ck2dJLt9SA==
rc-textarea@~1.9.0:
version "1.9.0"
resolved "https://registry.npmjs.org/rc-textarea/-/rc-textarea-1.9.0.tgz"
integrity sha512-dQW/Bc/MriPBTugj2Kx9PMS5eXCCGn2cxoIaichjbNvOiARlaHdI99j4DTxLl/V8+PIfW06uFy7kjfUIDDKyxQ==
dependencies:
"@babel/runtime" "^7.10.1"
classnames "^2.2.1"
rc-input "~1.8.0"
rc-input "~1.7.1"
rc-resize-observer "^1.0.0"
rc-util "^5.27.0"
@@ -10929,10 +10941,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.9.0:
version "4.9.0"
resolved "https://registry.yarnpkg.com/rc-upload/-/rc-upload-4.9.0.tgz#911963ab5a0b538c743765371c05e2de9e3f5436"
integrity sha512-pAzlPnyiFn1GCtEybEG2m9nXNzQyWXqWV2xFYCmDxjN9HzyjS5Pz2F+pbNdYw8mMJsixLEKLG0wVy9vOGxJMJA==
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==
dependencies:
"@babel/runtime" "^7.18.3"
classnames "^2.2.5"
@@ -13033,10 +13045,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.8:
version "5.99.8"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.99.8.tgz#dd31a020b7c092d30c4c6d9a4edb95809e7f5946"
integrity sha512-lQ3CPiSTpfOnrEGeXDwoq5hIGzSjmwD72GdfVzF7CQAI7t47rJG9eDWvcEkEn3CUQymAElVvDg3YNTlCYj+qUQ==
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==
dependencies:
"@types/eslint-scope" "^3.7.7"
"@types/estree" "^1.0.6"

View File

@@ -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.7.0, <5.0.0",
"flask-appbuilder>=4.6.3, <5.0.0",
"flask-caching>=2.1.0, <3",
"flask-compress>=1.13, <2.0",
"flask-talisman>=1.0.0, <2.0",
@@ -240,12 +240,6 @@ 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 = [
@@ -278,6 +272,7 @@ exclude = [
"venv",
]
# Same as Black.
line-length = 88
indent-width = 4
@@ -372,7 +367,6 @@ 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",
@@ -386,7 +380,6 @@ authorized_licenses = [
"osi approved",
"psf-2.0",
"python software foundation",
"simplified bsd",
"the unlicense (unlicense)",
"the unlicense",
]

View File

@@ -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.2
alembic==1.15.1
# 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.2.0
apsw==3.49.1.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.5.0
bottleneck==1.4.2
# 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.3
cattrs==24.1.2
# via requests-cache
celery==5.5.2
# via apache-superset (pyproject.toml)
certifi==2025.4.26
certifi==2025.1.31
# via
# requests
# selenium
@@ -54,9 +54,9 @@ cffi==1.17.1
# via
# cryptography
# pynacl
charset-normalizer==3.4.2
charset-normalizer==3.4.1
# via requests
click==8.2.0
click==8.1.8
# 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.3.0
exceptiongroup==1.2.2
# via
# cattrs
# trio
@@ -118,7 +118,7 @@ flask==2.3.3
# flask-session
# flask-sqlalchemy
# flask-wtf
flask-appbuilder==4.7.0
flask-appbuilder==4.6.3
# via apache-superset (pyproject.toml)
flask-babel==2.0.0
# via flask-appbuilder
@@ -152,12 +152,13 @@ geographiclib==2.0
# via geopy
geopy==2.4.1
# via apache-superset (pyproject.toml)
google-auth==2.40.1
google-auth==2.38.0
# 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
@@ -173,7 +174,6 @@ 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==2025.4.1
jsonschema-specifications==2024.10.1
# via jsonschema
kombu==5.5.3
# via celery
@@ -243,9 +243,7 @@ openpyxl==3.1.5
ordered-set==4.1.0
# via flask-limiter
outcome==1.3.0.post0
# via
# trio
# trio-websocket
# via trio
packaging==25.0
# via
# apache-superset (pyproject.toml)
@@ -265,7 +263,7 @@ parsedatetime==2.6
# via apache-superset (pyproject.toml)
pgsanity==0.2.9
# via apache-superset (pyproject.toml)
platformdirs==4.3.8
platformdirs==4.3.7
# via requests-cache
ply==3.11
# via jsonpath-ng
@@ -281,7 +279,7 @@ pyasn1==0.6.1
# via
# pyasn1-modules
# rsa
pyasn1-modules==0.4.2
pyasn1-modules==0.4.1
# via google-auth
pycparser==2.22
# via cffi
@@ -338,13 +336,13 @@ requests-cache==1.2.1
# via shillelagh
rich==13.9.4
# via flask-limiter
rpds-py==0.25.0
rpds-py==0.23.1
# via
# jsonschema
# referencing
rsa==4.9.1
rsa==4.9
# via google-auth
selenium==4.32.0
selenium==4.27.1
# via apache-superset (pyproject.toml)
shillelagh==1.3.5
# via apache-superset (pyproject.toml)
@@ -354,6 +352,7 @@ six==1.17.0
# via
# prison
# python-dateutil
# url-normalize
# wtforms-json
slack-sdk==3.35.0
# via apache-superset (pyproject.toml)
@@ -374,7 +373,7 @@ sqlalchemy-utils==0.38.3
# via
# apache-superset (pyproject.toml)
# flask-appbuilder
sqlglot==26.17.1
sqlglot==26.16.4
# via apache-superset (pyproject.toml)
sqlparse==0.5.3
# via apache-superset (pyproject.toml)
@@ -382,18 +381,17 @@ sshtunnel==0.4.0
# via apache-superset (pyproject.toml)
tabulate==0.8.10
# via apache-superset (pyproject.toml)
trio==0.30.0
trio==0.28.0
# via
# selenium
# trio-websocket
trio-websocket==0.12.2
trio-websocket==0.11.1
# via selenium
typing-extensions==4.13.2
typing-extensions==4.12.2
# via
# apache-superset (pyproject.toml)
# alembic
# cattrs
# exceptiongroup
# limits
# pyopenssl
# referencing
@@ -404,7 +402,7 @@ tzdata==2025.2
# via
# kombu
# pandas
url-normalize==2.2.1
url-normalize==1.4.3
# via requests-cache
urllib3==1.26.20
# via

View File

@@ -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.2
alembic==1.15.1
# 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.2.0
apsw==3.49.1.0
# via
# -c requirements/base.txt
# shillelagh
@@ -51,7 +51,7 @@ blinker==1.9.0
# via
# -c requirements/base.txt
# flask
bottleneck==1.5.0
bottleneck==1.4.2
# 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.3
cattrs==24.1.2
# via
# -c requirements/base.txt
# requests-cache
@@ -76,7 +76,7 @@ celery==5.5.2
# via
# -c requirements/base.txt
# apache-superset
certifi==2025.4.26
certifi==2025.1.31
# 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.2
charset-normalizer==3.4.1
# via
# -c requirements/base.txt
# requests
click==8.2.0
click==8.1.8
# via
# -c requirements/base.txt
# apache-superset
@@ -176,7 +176,7 @@ et-xmlfile==2.0.0
# via
# -c requirements/base.txt
# openpyxl
exceptiongroup==1.3.0
exceptiongroup==1.2.2
# via
# -c requirements/base.txt
# cattrs
@@ -202,7 +202,7 @@ flask==2.3.3
# flask-sqlalchemy
# flask-testing
# flask-wtf
flask-appbuilder==4.7.0
flask-appbuilder==4.6.3
# 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.40.1
google-auth==2.38.0
# via
# -c requirements/base.txt
# google-api-core
@@ -318,6 +318,7 @@ greenlet==3.1.1
# apache-superset
# gevent
# shillelagh
# sqlalchemy
grpcio==1.71.0
# via
# apache-superset
@@ -354,7 +355,6 @@ 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==2025.4.1
jsonschema-specifications==2024.10.1
# via
# -c requirements/base.txt
# jsonschema
@@ -495,7 +495,6 @@ outcome==1.3.0.post0
# via
# -c requirements/base.txt
# trio
# trio-websocket
packaging==25.0
# via
# -c requirements/base.txt
@@ -543,7 +542,7 @@ pillow==10.3.0
# via
# apache-superset
# matplotlib
platformdirs==4.3.8
platformdirs==4.3.7
# via
# -c requirements/base.txt
# requests-cache
@@ -599,7 +598,7 @@ pyasn1==0.6.1
# pyasn1-modules
# python-ldap
# rsa
pyasn1-modules==0.4.2
pyasn1-modules==0.4.1
# via
# -c requirements/base.txt
# google-auth
@@ -732,22 +731,22 @@ rich==13.9.4
# via
# -c requirements/base.txt
# flask-limiter
rpds-py==0.25.0
rpds-py==0.23.1
# via
# -c requirements/base.txt
# jsonschema
# referencing
rsa==4.9.1
rsa==4.9
# via
# -c requirements/base.txt
# google-auth
ruff==0.8.0
# via apache-superset
selenium==4.32.0
selenium==4.27.1
# via
# -c requirements/base.txt
# apache-superset
setuptools==80.7.1
setuptools==75.6.0
# via
# nodeenv
# pandas-gbq
@@ -768,6 +767,7 @@ 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.17.1
sqlglot==26.16.4
# via
# -c requirements/base.txt
# apache-superset
@@ -829,22 +829,21 @@ tqdm==4.67.1
# prophet
trino==0.330.0
# via apache-superset
trio==0.30.0
trio==0.28.0
# via
# -c requirements/base.txt
# selenium
# trio-websocket
trio-websocket==0.12.2
trio-websocket==0.11.1
# via
# -c requirements/base.txt
# selenium
typing-extensions==4.13.2
typing-extensions==4.12.2
# via
# -c requirements/base.txt
# alembic
# apache-superset
# cattrs
# exceptiongroup
# limits
# pyopenssl
# referencing
@@ -858,7 +857,7 @@ tzdata==2025.2
# pandas
tzlocal==5.2
# via trino
url-normalize==2.2.1
url-normalize==1.4.3
# via
# -c requirements/base.txt
# requests-cache

View File

@@ -89,7 +89,6 @@ export type EmbeddedDashboard = {
callbackFn: ObserveDataMaskCallbackFn,
) => void;
getDataMask: () => Record<string, any>;
setThemeConfig: (themeConfig: Record<string, any>) => void;
};
/**
@@ -246,18 +245,6 @@ 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,
@@ -266,6 +253,5 @@ export async function embedDashboard({
getActiveTabs,
observeDataMask,
getDataMask,
setThemeConfig
};
}

View File

@@ -19,6 +19,7 @@
coverage/**
dist/*
src/assets/images/*
src/assets/stylesheets/*
node_modules/*
node_modules*/*
vendor/*

View File

@@ -52,7 +52,7 @@ const restrictedImportsRules = {
message: 'Lodash Memoize is unsafe! Please use memoize-one instead',
},
'no-testing-library-react': {
name: '@superset-ui/core/spec',
name: '@testing-library/react',
message: 'Please use spec/helpers/testing-library instead',
},
'no-testing-library-react-dom-utils': {
@@ -63,6 +63,10 @@ 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'],
@@ -97,15 +101,6 @@ 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,
@@ -244,14 +239,6 @@ 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',
@@ -308,9 +295,9 @@ module.exports = {
'error',
{
paths: Object.values(restrictedImportsRules).filter(
r => r.name !== 'antd',
r => r.name !== 'antd-v5',
),
patterns: [],
patterns: ['antd/*'],
},
],
},
@@ -343,9 +330,7 @@ module.exports = {
rules: {
'import/no-extraneous-dependencies': [
'error',
{
devDependencies: true,
},
{ devDependencies: true },
],
'no-only-tests/no-only-tests': 'error',
'max-classes-per-file': 0,
@@ -388,7 +373,7 @@ module.exports = {
'fixtures.*',
'cypress-base/cypress/**/*',
'Stories.tsx',
'packages/superset-ui-core/src/theme/index.tsx',
'packages/superset-ui-core/src/style/index.tsx',
],
rules: {
'theme-colors/no-literal-colors': 0,

View File

@@ -17,75 +17,31 @@
* under the License.
*/
import { withJsx } from '@mihkeleidast/storybook-addon-source';
import { themeObject, css, exampleThemes } from '@superset-ui/core';
import { supersetTheme, ThemeProvider } from '@superset-ui/core';
import { AntdThemeProvider } from '../src/components/AntdThemeProvider';
import { combineReducers, createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import { Provider } from 'react-redux';
import reducerIndex from 'spec/helpers/reducerIndex';
import { Global } from '@emotion/react';
import { App, Layout, Space, Content } from 'antd';
import { GlobalStyles } from '../src/GlobalStyles';
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)),
);
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 themeDecorator = Story => (
<ThemeProvider theme={supersetTheme}>
<AntdThemeProvider>
<GlobalStyles />
<Story />
</AntdThemeProvider>
</ThemeProvider>
);
const providerDecorator = Story => (
<Provider store={store}>
@@ -125,5 +81,5 @@ export const parameters = {
],
},
},
controls: { expanded: true, sort: 'alpha', disableSaveFromUI: true },
controls: { expanded: true, sort: 'alpha' },
};

View File

@@ -1,2 +1,3 @@
body {
background: transparent;
}

View File

@@ -1,56 +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 { 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);
});
});

View File

@@ -35,12 +35,12 @@ function orderAlphabetical() {
}
function openProperties() {
cy.get('[aria-label="more"]').eq(0).click();
cy.get('[aria-label="more"]').eq(1).click();
cy.getBySel('chart-list-edit-option').click();
}
function openMenu() {
cy.get('[aria-label="more"]').eq(0).click();
cy.get('[aria-label="more"]').eq(1).click();
}
function confirmDelete() {
@@ -81,13 +81,12 @@ describe('Charts list', () => {
cy.wait('@get');
});
it('should show the newly added dashboards in a tooltip', () => {
it.only('should show the newly added dashboards in a tooltip', () => {
interceptDashboardGet();
visitSampleChartFromList('1 - Sample chart');
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');
saveChartToDashboard('1 - Sample dashboard');
saveChartToDashboard('2 - Sample dashboard');
saveChartToDashboard('3 - Sample dashboard');
visitChartList();
cy.getBySel('count-crosslinks').should('be.visible');
@@ -96,6 +95,8 @@ describe('Charts list', () => {
describe('list mode', () => {
before(() => {
cy.createSampleDashboards([0, 1, 2, 3]);
cy.createSampleCharts([0]);
visitChartList();
setGridMode('list');
});
@@ -111,10 +112,18 @@ 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('[aria-label="Select all"]').click();
cy.get('input[type="checkbox"]:checked').should('have.length', 26);
cy.get('#header-toggle-all').click();
cy.get('[aria-label="checkbox-on"]').should('have.length', 26);
cy.getBySel('bulk-select-copy').contains('25 Selected');
cy.getBySel('bulk-select-action')
.should('have.length', 2)
@@ -123,7 +132,7 @@ describe('Charts list', () => {
expect($btns).to.contain('Export');
});
cy.getBySel('bulk-select-deselect-all').click();
cy.get('input[type="checkbox"]:checked').should('have.length', 0);
cy.get('[aria-label="checkbox-on"]').should('have.length', 0);
cy.getBySel('bulk-select-copy').contains('0 Selected');
cy.getBySel('bulk-select-action').should('not.exist');
});
@@ -155,6 +164,11 @@ 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');
@@ -165,12 +179,40 @@ describe('Charts list', () => {
describe('common actions', () => {
beforeEach(() => {
cy.createSampleCharts([0, 1, 2, 3]);
visitChartList();
});
it('should bulk delete correctly', () => {
cy.createSampleCharts([0, 1, 2, 3]);
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', () => {
interceptBulkDelete();
toggleBulkSelect();
@@ -178,10 +220,9 @@ describe('Charts list', () => {
setGridMode('card');
orderAlphabetical();
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();
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();
confirmDelete();
cy.wait('@bulkDelete');
cy.getBySel('styled-card')
@@ -193,71 +234,56 @@ describe('Charts list', () => {
// bulk deletes in list-view
setGridMode('list');
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.getBySel('table-row').eq(1).contains('3 - Sample chart');
cy.getBySel('table-row').eq(2).contains('4 - Sample chart');
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.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');
cy.getBySel('table-row').eq(1).should('not.contain', '3 - Sample chart');
cy.getBySel('table-row').eq(2).should('not.contain', '4 - Sample chart');
});
it('should delete correctly in card mode', () => {
cy.createSampleCharts([0, 1]);
it('should delete correctly', () => {
interceptDelete();
// deletes in card-view
setGridMode('card');
orderAlphabetical();
cy.getBySel('styled-card').contains('1 - Sample chart');
cy.getBySel('styled-card').eq(1).contains('1 - Sample chart');
openMenu();
cy.getBySel('chart-list-delete-option').click();
confirmDelete();
cy.wait('@delete');
cy.getBySel('styled-card')
.contains('1 - Sample chart')
.should('not.exist');
});
.eq(1)
.should('not.contain', '1 - Sample chart');
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();
// deletes in list-view
setGridMode('list');
cy.getBySel('table-row').eq(1).contains('2 - Sample chart');
cy.getBySel('delete').eq(1).click();
confirmDelete();
cy.wait('@delete');
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', '2 - Sample chart');
});
it('should edit correctly', () => {
cy.createSampleCharts([0]);
interceptUpdate();
// edits in card-view
setGridMode('card');
orderAlphabetical();
cy.getBySel('skeleton-card').should('not.exist');
cy.getBySel('styled-card').eq(0).contains('1 - Sample chart');
cy.getBySel('styled-card').eq(1).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(0).contains('1 - Sample chart | EDITED');
cy.getBySel('styled-card').eq(1).contains('1 - Sample chart | EDITED');
// edits in list-view
setGridMode('list');

View File

@@ -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',
'ant-dropdown-menu-item-disabled',
'antd5-dropdown-menu-item-disabled',
);
waitForChartLoad(mapSpec);
cy.get('[data-test="refresh-chart-menu-item"]').should(
'not.have.class',
'ant-dropdown-menu-item-disabled',
'antd5-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',
'ant-dropdown-menu-item-disabled',
'antd5-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',
'ant-dropdown-menu-item-disabled',
'antd5-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',
'ant-dropdown-menu-item-disabled',
'antd5-dropdown-menu-item-disabled',
);
});
});

View File

@@ -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(133, 133, 133)');
.and('eq', 'rgb(178, 178, 178)');
});
});

View File

@@ -54,14 +54,14 @@ const drillBy = (targetDrillByColumn: string, isLegacy = false) => {
interceptV1ChartData();
}
cy.get('.ant-dropdown:not(.ant-dropdown-hidden)')
cy.get('.antd5-dropdown:not(.antd5-dropdown-hidden)')
.should('be.visible')
.find("[role='menu'] [role='menuitem']")
.contains(/^Drill by$/)
.trigger('mouseover', { force: true });
cy.get(
'.ant-dropdown-menu-submenu:not(.ant-dropdown-menu-submenu-hidden) [data-test="drill-by-submenu"]',
'.antd5-dropdown-menu-submenu:not(.antd5-dropdown-menu-submenu-hidden) [data-test="drill-by-submenu"]',
)
.should('be.visible')
.find('[role="menuitem"]')

View File

@@ -34,8 +34,8 @@ function openModalFromMenu(chartType: string) {
cy.get(
`[data-test-viz-type='${chartType}'] [aria-label='More Options']`,
).click();
cy.get('.ant-dropdown')
.not('.ant-dropdown-hidden')
cy.get('.antd5-dropdown')
.not('.antd5-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('.ant-dropdown')
.not('.ant-dropdown-hidden')
cy.get('.antd5-dropdown')
.not('.antd5-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('.ant-dropdown:not(.ant-dropdown-hidden)')
cy.get('.antd5-dropdown:not(.antd5-dropdown-hidden)')
.should('be.visible')
.find("[role='menu'] [role='menuitem']")
.contains(/^Drill to detail by$/)
.trigger('mouseover', { force: true });
cy.get(
'.ant-dropdown-menu-submenu:not(.ant-dropdown-menu-submenu-hidden) [data-test="drill-to-detail-by-submenu"]',
'.antd5-dropdown-menu-submenu:not(.antd5-dropdown-menu-submenu-hidden) [data-test="drill-to-detail-by-submenu"]',
)
.should('be.visible')
.find('[role="menuitem"]')
@@ -121,10 +121,7 @@ function testTimeChart(vizType: string) {
});
}
// 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', () => {
describe('Drill to detail modal', () => {
beforeEach(() => {
closeModal();
});
@@ -466,7 +463,7 @@ describe.skip('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');

View File

@@ -17,12 +17,7 @@
* under the License.
*/
import { SAMPLE_DASHBOARD_1, TABBED_DASHBOARD } from 'cypress/utils/urls';
import {
drag,
resize,
setSelectSearchInput,
waitForChartLoad,
} from 'cypress/utils';
import { drag, resize, waitForChartLoad } from 'cypress/utils';
import { edit } from 'brace';
import {
interceptExploreUpdate,
@@ -39,12 +34,21 @@ function editDashboard() {
cy.getBySel('edit-dashboard-button').click();
}
function closeModal() {
cy.getBySel('properties-modal-cancel-button').click({ force: true });
}
function openProperties() {
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');
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');
});
}
function assertMetadata(text: string) {
@@ -61,7 +65,7 @@ function assertMetadata(text: string) {
}
function openAdvancedProperties() {
cy.get('.ant-modal-body')
cy.get('.antd5-modal-body')
.contains('Advanced')
.should('be.visible')
.click({ force: true });
@@ -146,10 +150,12 @@ function selectColorScheme(
target = 'dashboard-edit-properties-form',
) {
cy.get(`[data-test="${target}"] input[aria-label="Select color scheme"]`)
.should('exist')
.first()
.then($input => {
setSelectSearchInput($input, color.slice(0, 5));
cy.wrap($input).click({ force: true });
cy.wrap($input).type(color.slice(0, 5), { force: true });
});
cy.getBySel(color).click({ force: true });
}
function saveAndGo(dashboard = 'Tabbed Dashboard') {
@@ -1089,7 +1095,7 @@ describe('Dashboard edit', () => {
cy.allowConsoleErrors(['Error: A valid color scheme is required']);
writeMetadata('{"color_scheme":"wrongcolorscheme"}');
applyChanges();
cy.get('.ant-modal-body')
cy.get('.antd5-modal-body')
.contains('A valid color scheme is required')
.should('be.visible');
});
@@ -1124,7 +1130,7 @@ describe('Dashboard edit', () => {
it('should filter charts', () => {
interceptCharts();
cy.get('input[type="checkbox"]').click();
cy.get('[role="checkbox"]').click();
cy.getBySel('dashboard-charts-filter-search-input').type('Unicode');
cy.wait('@filtering');
cy.getBySel('chart-card')
@@ -1134,8 +1140,8 @@ describe('Dashboard edit', () => {
});
// TODO fix this test! This was the #1 flaky test as of 4/21/23 according to cypress dashboard.
it.skip('should disable the Save button when undoing', () => {
cy.get('input[type="checkbox"]').click();
xit('should disable the Save button when undoing', () => {
cy.get('[role="checkbox"]').click();
dragComponent('Unicode Cloud', 'card-title', false);
cy.getBySel('header-save-button').should('be.enabled');
discardChanges();
@@ -1149,13 +1155,13 @@ describe('Dashboard edit', () => {
});
it('should add charts', () => {
cy.get('input[type="checkbox"]').click();
cy.get('[role="checkbox"]').click();
dragComponent();
cy.getBySel('dashboard-component-chart-holder').should('have.length', 1);
});
it.skip('should remove added charts', () => {
cy.get('input[type="checkbox"]').click();
cy.get('[role="checkbox"]').click();
dragComponent('Unicode Cloud');
cy.getBySel('dashboard-component-chart-holder').should('have.length', 1);
cy.getBySel('dashboard-delete-component-button').click();
@@ -1198,7 +1204,7 @@ describe('Dashboard edit', () => {
});
it('should save', () => {
cy.get('input[type="checkbox"]').click();
cy.get('[role="checkbox"]').click();
dragComponent();
cy.getBySel('header-save-button').should('be.enabled');
saveChanges();

View File

@@ -57,16 +57,16 @@ function setFilterBarOrientation(orientation: 'vertical' | 'horizontal') {
.trigger('mouseover');
if (orientation === 'vertical') {
cy.get('.ant-dropdown-menu-item-selected')
cy.get('.antd5-dropdown-menu-item-selected')
.contains('Horizontal (Top)')
.should('exist');
cy.get('.ant-dropdown-menu-item').contains('Vertical (Left)').click();
cy.get('.antd5-dropdown-menu-item').contains('Vertical (Left)').click();
cy.getBySel('dashboard-filters-panel').should('exist');
} else {
cy.get('.ant-dropdown-menu-item-selected')
cy.get('.antd5-dropdown-menu-item-selected')
.contains('Vertical (Left)')
.should('exist');
cy.get('.ant-dropdown-menu-item').contains('Horizontal (Top)').click();
cy.get('.antd5-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.only('should show "more filters" and scroll', () => {
it('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_3').should('be.visible');
cy.getBySel('filter-control-name').contains('test_10').should('be.visible');
cy.getBySel('filter-control-name')
.contains('test_12')
.should('not.be.visible');
cy.getBySel('filter-control-name').contains('test_12').scrollIntoView();
cy.get('.antd5-popover-inner').scrollTo('bottom');
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('.ant-scroll-number.ant-badge-count').should(
cy.get('.antd5-scroll-number.antd5-badge-count').should(
'have.attr',
'title',
'1',

View File

@@ -199,16 +199,14 @@ describe('Native filters', () => {
.should('be.visible')
.click();
cy.get('[data-test="range-filter-from-input"]').type('{selectall}40');
cy.get('[data-test="range-filter-from-input"]').type('{selectall}5');
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({
force: true,
});
cy.get(nativeFilters.applyFilter).click();
// Assert that the URL contains 'native_filters'
cy.url().then(u => {
@@ -217,7 +215,7 @@ describe('Native filters', () => {
cy.get('[data-test="range-filter-from-input"]')
.invoke('val')
.should('equal', '40');
.should('equal', '5');
// Assert that the "To" input has the correct value
cy.get('[data-test="range-filter-to-input"]')

View File

@@ -293,11 +293,7 @@ describe('Native filters', () => {
it('Verify setting options and tooltips for value filter', () => {
enterNativeFilterEditModal(false);
cy.contains('Filter value is required').scrollIntoView();
cy.contains('Filter value is required').should('be.visible').click({
force: true,
});
cy.contains('Filter value is required').should('be.visible').click();
checkNativeFilterTooltip(0, nativeFilterTooltips.preFilter);
checkNativeFilterTooltip(1, nativeFilterTooltips.defaultValue);
cy.get(nativeFilters.modal.container).should('be.visible');

View File

@@ -18,11 +18,7 @@
*/
import { dashboardView, nativeFilters } from 'cypress/support/directories';
import {
ChartSpec,
setSelectSearchInput,
waitForChartLoad,
} from 'cypress/utils';
import { ChartSpec, waitForChartLoad } from 'cypress/utils';
export const WORLD_HEALTH_CHARTS = [
{ name: '% Rural', viz: 'world_map' },
@@ -268,11 +264,10 @@ export function fillNativeFilterForm(
dataset?: string,
filterColumn?: string,
) {
cy.get(nativeFilters.filtersPanel.filterTypeInput).within(() => {
cy.get('input').then($input => {
setSelectSearchInput($input, type);
});
});
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.modal.container)
.find(nativeFilters.filtersPanel.filterName)
.last()
@@ -285,23 +280,31 @@ export function fillNativeFilterForm(
.find(nativeFilters.filtersPanel.filterName)
.last()
.type(name, { scrollBehavior: false, force: true });
if (dataset) {
cy.get('div[aria-label="Dataset"]').within(() => {
cy.get('input').then($input => {
setSelectSearchInput($input, dataset, true);
});
});
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(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('div[aria-label="Column select"]').within(() => {
cy.get('input').then($input => {
setSelectSearchInput($input, filterColumn, true);
});
});
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(nativeFilters.silentLoading).should('not.exist');
}
/** ************************************************************************
@@ -439,9 +442,6 @@ 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('.ant-popover-content .ant-select-selector')
cy.get('.control-label').contains('RANGE TYPE').should('be.visible');
cy.get('.antd5-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('.ant-popover-inner-content')
cy.get('.antd5-popover-inner-content')
.find('[class^=ant-input]')
.first()
.type(`${startRange}`);
}
if (endRange) {
cy.get('.ant-popover-inner-content')
cy.get('.antd5-popover-inner-content')
.find('[class^=ant-input]')
.last()
.type(`${endRange}`);

View File

@@ -79,20 +79,16 @@ 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('[aria-label="Select all"]').click();
cy.get('.ant-checkbox-input')
.should('be.checked')
.should('have.length', 6);
cy.get('#header-toggle-all').click();
cy.get('[aria-label="checkbox-on"]').should('have.length', 6);
cy.getBySel('bulk-select-copy').contains('5 Selected');
cy.getBySel('bulk-select-action')
.should('have.length', 2)
@@ -101,7 +97,7 @@ describe('Dashboards list', () => {
expect($btns).to.contain('Export');
});
cy.getBySel('bulk-select-deselect-all').click();
cy.get('input[type="checkbox"]:checked').should('have.length', 0);
cy.get('[aria-label="checkbox-on"]').should('have.length', 0);
cy.getBySel('bulk-select-copy').contains('0 Selected');
cy.getBySel('bulk-select-action').should('not.exist');
});
@@ -199,8 +195,6 @@ 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');

View File

@@ -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').should('be.visible').click();
cy.get('.ant-dropdown-menu-submenu-title')
cy.getBySel('actions-trigger').click();
cy.get('.antd5-dropdown-menu-submenu-title')
.contains('On dashboards')
.trigger('mouseover', { force: true });
}
function closeDashboardsAddedTo() {
cy.get('.ant-dropdown-menu-submenu-title')
cy.get('.antd5-dropdown-menu-submenu-title')
.contains('On dashboards')
.trigger('mouseout', { force: true });
cy.getBySel('actions-trigger').click();
}
function verifyDashboardsSubmenuItem(dashboardName) {
cy.get('.ant-dropdown-menu-submenu-popup').contains(dashboardName);
cy.get('.antd5-dropdown-menu-submenu-popup').contains(dashboardName);
closeDashboardsAddedTo();
}
function verifyDashboardSearch() {
openDashboardsAddedTo();
cy.get('.ant-dropdown-menu-submenu-popup').trigger('mouseover');
cy.get('.ant-dropdown-menu-submenu-popup')
cy.get('.antd5-dropdown-menu-submenu-popup').trigger('mouseover');
cy.get('.antd5-dropdown-menu-submenu-popup')
.find('input[placeholder="Search"]')
.type('1');
cy.get('.ant-dropdown-menu-submenu-popup').contains('1 - Sample dashboard');
cy.get('.ant-dropdown-menu-submenu-popup')
cy.get('.antd5-dropdown-menu-submenu-popup').contains('1 - Sample dashboard');
cy.get('.antd5-dropdown-menu-submenu-popup')
.find('input[placeholder="Search"]')
.type('Blahblah');
cy.get('.ant-dropdown-menu-submenu-popup').contains('No results found');
cy.get('.ant-dropdown-menu-submenu-popup')
cy.get('.antd5-dropdown-menu-submenu-popup').contains('No results found');
cy.get('.antd5-dropdown-menu-submenu-popup')
.find('[aria-label="close-circle"]')
.click();
closeDashboardsAddedTo();
@@ -68,8 +68,8 @@ function verifyDashboardSearch() {
function verifyDashboardLink() {
interceptDashboardGet();
openDashboardsAddedTo();
cy.get('.ant-dropdown-menu-submenu-popup').trigger('mouseover');
cy.get('.ant-dropdown-menu-submenu-popup a')
cy.get('.antd5-dropdown-menu-submenu-popup').trigger('mouseover');
cy.get('.antd5-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(chartName, number) {
saveChartToDashboard(chartName, `${number} - Sample dashboard`);
function saveAndVerifyDashboard(number) {
saveChartToDashboard(`${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 - 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');
saveAndVerifyDashboard('1');
saveAndVerifyDashboard('2');
saveAndVerifyDashboard('3');
saveAndVerifyDashboard('4');
saveAndVerifyDashboard('5');
saveAndVerifyDashboard('6');
saveAndVerifyDashboard('7');
saveAndVerifyDashboard('8');
saveAndVerifyDashboard('9');
saveAndVerifyDashboard('10');
saveAndVerifyDashboard('11');
verifyDashboardSearch();
verifyDashboardLink();

View File

@@ -19,7 +19,7 @@
// ***********************************************
// Tests for setting controls in the UI
// ***********************************************
import { interceptChart, setSelectSearchInput } from 'cypress/utils';
import { interceptChart } 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('.ant-table-body [data-test="textarea-editable-title-input"]')
cy.get(
'[data-test="table-content-rows"] [data-test="editable-title-input"]',
)
.first()
.click();
cy.get('.ant-table-body [data-test="textarea-editable-title-input"]')
cy.get(
'[data-test="table-content-rows"] [data-test="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('.ant-modal-confirm-btns button').contains('OK').click();
cy.get('.antd5-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"]')
.should('exist')
.then($input => {
setSelectSearchInput($input, newMetricName);
});
cy.get('input[aria-label="Select saved metrics"]').type(
`${newMetricName}{enter}`,
);
// delete metric
cy.get('[data-test="datasource-menu-trigger"]').click();
cy.get('[data-test="edit-dataset"]').click();
cy.get('.ant-modal-content').within(() => {
cy.get('.antd5-modal-content').within(() => {
cy.get('[data-test="collection-tab-Metrics"]')
.contains('Metrics')
.click();
});
cy.get(`[data-test="textarea-editable-title-input"]`)
.contains(newMetricName)
cy.get(`input[value="${newMetricName}"]`)
.closest('tr')
.find('[data-test="crud-delete-icon"]')
.click();
cy.get('[data-test="datasource-modal-save"]').click();
cy.get('.ant-modal-confirm-btns button').contains('OK').click();
cy.get('.antd5-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-CUSTOMIZE').click();
cy.get('#controlSections-tab-display').click();
cy.get('.ant-select-selection-item .color-scheme-label').contains(
'Superset Colors',
);
@@ -102,19 +102,10 @@ 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', { force: true });
cy.get('.color-scheme-tooltip').should('not.be.visible');
cy.getBySel('lyftColors').trigger('mouseover');
cy.get('.color-scheme-tooltip').should('not.exist');
});
});
describe('VizType control', () => {
@@ -129,7 +120,7 @@ describe('VizType control', () => {
cy.contains('View all charts').click();
cy.get('.ant-modal-content').within(() => {
cy.get('.antd5-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();

View File

@@ -42,8 +42,8 @@ describe('Test explore links', () => {
cy.wait('@chartData').then(() => {
cy.get('code');
});
cy.get('.ant-modal-content').within(() => {
cy.get('button.ant-modal-close').first().click({ force: true });
cy.get('.antd5-modal-content').within(() => {
cy.get('button.antd5-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[title="${dashboardTitle}"]`).click({
cy.get(`.ant-select-item[label="${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[title="${dashboardTitle}"]`).click({
cy.get(`.ant-select-item[label="${dashboardTitle}"]`).click({
force: true,
});

View File

@@ -67,7 +67,7 @@ export function setFilter(filter: string, option: string) {
cy.wait('@filtering');
}
export function saveChartToDashboard(chartName: string, dashboardName: string) {
export function saveChartToDashboard(dashboardName: string) {
interceptDashboardGet();
interceptUpdate();
interceptExploreGet();
@@ -75,30 +75,23 @@ export function saveChartToDashboard(chartName: string, dashboardName: string) {
cy.getBySel('query-save-button')
.should('be.enabled')
.should('not.be.disabled')
.click({ force: true });
.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();
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('@update');
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) {

View File

@@ -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-CUSTOMIZE').click();
cy.get('#controlSections-tab-display').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}');

View File

@@ -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-CUSTOMIZE').click();
cy.get('#controlSections-tab-display').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}');

View File

@@ -36,10 +36,10 @@ describe('Download Chart > Bar chart', () => {
};
cy.visitChartByParams(formData);
cy.get('.header-with-actions .ant-dropdown-trigger').click();
cy.get(':nth-child(3) > .ant-dropdown-menu-submenu-title').click();
cy.get('.header-with-actions .antd5-dropdown-trigger').click();
cy.get(':nth-child(3) > .antd5-dropdown-menu-submenu-title').click();
cy.get(
'.ant-dropdown-menu-submenu > .ant-dropdown-menu li:nth-child(3)',
'.antd5-dropdown-menu-submenu > .antd5-dropdown-menu li:nth-child(3)',
).click();
cy.verifyDownload('.jpg', {
contains: true,

View File

@@ -64,7 +64,7 @@ describe('Visualization > Gauge', () => {
it('should allow type to search color schemes', () => {
verify(GAUGE_FORM_DATA);
cy.get('#controlSections-tab-CUSTOMIZE').click();
cy.get('#controlSections-tab-display').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}');

View File

@@ -80,7 +80,7 @@ describe('Visualization > Graph', () => {
it('should allow type to search color schemes', () => {
verify(GRAPH_FORM_DATA);
cy.get('#controlSections-tab-CUSTOMIZE').click();
cy.get('#controlSections-tab-display').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}');

View File

@@ -71,7 +71,7 @@ describe('Visualization > Pie', () => {
it('should allow type to search color schemes', () => {
verify(PIE_FORM_DATA);
cy.get('#controlSections-tab-CUSTOMIZE').click();
cy.get('#controlSections-tab-display').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}');

View File

@@ -86,7 +86,7 @@ describe('Visualization > Sunburst', () => {
it('should allow type to search color schemes', () => {
verify(SUNBURST_FORM_DATA);
cy.get('#controlSections-tab-CUSTOMIZE').click();
cy.get('#controlSections-tab-display').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}');

View File

@@ -193,9 +193,7 @@ describe('Visualization > Table', () => {
});
// should display in raw records mode
cy.get(
'div[data-test="query_mode"] .ant-radio-button-wrapper-checked',
).contains('Raw records');
cy.get('div[data-test="query_mode"] .btn.active').contains('Raw records');
cy.get('div[data-test="all_columns"]').should('be.visible');
cy.get('div[data-test="groupby"]').should('not.exist');
@@ -203,12 +201,8 @@ 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"] .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="query_mode"] .btn').contains('Aggregate').click();
cy.get('div[data-test="query_mode"] .btn.active').contains('Aggregate');
cy.get('div[data-test="all_columns"]').should('not.exist');
cy.get('div[data-test="groupby"]').should('be.visible');
});
@@ -258,217 +252,4 @@ describe('Visualization > Table', () => {
});
cy.get('td').contains(/\d*%/);
});
it('Test row limit with server pagination toggle', () => {
const serverPaginationSelector =
'[data-test="server_pagination-header"] div.pull-left [type="checkbox"]';
cy.visitChartByParams({
...VIZ_DEFAULTS,
metrics: ['count'],
row_limit: 100,
});
// Enable server pagination
cy.get(serverPaginationSelector).click();
// Click row limit control and select high value (200k)
cy.get('div[aria-label="Row limit"]').click();
// Type 200000 and press enter to select the option
cy.get('div[aria-label="Row limit"]')
.find('.ant-select-selection-search-input:visible')
.type('200000{enter}');
// Verify that there is no error tooltip when server pagination is enabled
cy.get('[data-test="error-tooltip"]').should('not.exist');
// Disable server pagination
cy.get(serverPaginationSelector).click();
// Verify error tooltip appears
cy.get('[data-test="error-tooltip"]').should('be.visible');
// Trigger mouseover and verify tooltip text
cy.get('[data-test="error-tooltip"]').trigger('mouseover');
// Verify tooltip content
cy.get('.ant-tooltip-inner').should('be.visible');
cy.get('.ant-tooltip-inner').should(
'contain',
'Server pagination needs to be enabled for values over',
);
// Hide the tooltip by adding display:none style
cy.get('.ant-tooltip').invoke('attr', 'style', 'display: none');
// Enable server pagination again
cy.get(serverPaginationSelector).click();
cy.get('[data-test="error-tooltip"]').should('not.exist');
cy.get('div[aria-label="Row limit"]').click();
// Type 1000000
cy.get('div[aria-label="Row limit"]')
.find('.ant-select-selection-search-input:visible')
.type('1000000');
// Wait for 1 second
cy.wait(1000);
// Press enter
cy.get('div[aria-label="Row limit"]')
.find('.ant-select-selection-search-input:visible')
.type('{enter}');
// Wait for error tooltip to appear and verify its content
cy.get('[data-test="error-tooltip"]')
.should('be.visible')
.trigger('mouseover');
// Wait for tooltip content and verify
cy.get('.ant-tooltip-inner').should('exist');
cy.get('.ant-tooltip-inner').should('be.visible');
// Verify tooltip content separately
cy.get('.ant-tooltip-inner').should('contain', 'Value cannot exceed');
});
it('Test sorting with server pagination enabled', () => {
cy.visitChartByParams({
...VIZ_DEFAULTS,
metrics: ['count'],
groupby: ['name'],
row_limit: 100000,
server_pagination: true, // Enable server pagination
});
// Wait for the initial data load
cy.wait('@chartData');
// Get the first column header (name)
cy.get('.chart-container th').contains('name').as('nameHeader');
// Click to sort ascending
cy.get('@nameHeader').click();
cy.wait('@chartData');
// Verify first row starts with 'A'
cy.get('.chart-container td:first').invoke('text').should('match', /^[Aa]/);
// Click again to sort descending
cy.get('@nameHeader').click();
cy.wait('@chartData');
// Verify first row starts with 'Z'
cy.get('.chart-container td:first').invoke('text').should('match', /^[Zz]/);
// Test numeric sorting
cy.get('.chart-container th').contains('COUNT').as('countHeader');
// Click to sort ascending by count
cy.get('@countHeader').click();
cy.wait('@chartData');
// Get first two count values and verify ascending order
cy.get('.chart-container td:nth-child(2)').then($cells => {
const first = parseFloat($cells[0].textContent || '0');
const second = parseFloat($cells[1].textContent || '0');
expect(first).to.be.at.most(second);
});
// Click again to sort descending
cy.get('@countHeader').click();
cy.wait('@chartData');
// Get first two count values and verify descending order
cy.get('.chart-container td:nth-child(2)').then($cells => {
const first = parseFloat($cells[0].textContent || '0');
const second = parseFloat($cells[1].textContent || '0');
expect(first).to.be.at.least(second);
});
});
it('Test search with server pagination enabled', () => {
cy.visitChartByParams({
...VIZ_DEFAULTS,
metrics: ['count'],
groupby: ['name', 'state'],
row_limit: 100000,
server_pagination: true,
include_search: true,
});
cy.wait('@chartData');
const searchInputSelector = '.dt-global-filter input';
// Basic search test
cy.get(searchInputSelector).should('be.visible');
cy.get(searchInputSelector).type('John');
cy.wait('@chartData');
cy.get('.chart-container tbody tr').each($row => {
cy.wrap($row).contains(/John/i);
});
// Clear and test case-insensitive search
cy.get(searchInputSelector).clear();
cy.wait('@chartData');
cy.get(searchInputSelector).type('mary');
cy.wait('@chartData');
cy.get('.chart-container tbody tr').each($row => {
cy.wrap($row).contains(/Mary/i);
});
// Test special characters
cy.get(searchInputSelector).clear();
cy.get(searchInputSelector).type('Nicole');
cy.wait('@chartData');
cy.get('.chart-container tbody tr').each($row => {
cy.wrap($row).contains(/Nicole/i);
});
// Test no results
cy.get(searchInputSelector).clear();
cy.get(searchInputSelector).type('XYZ123');
cy.wait('@chartData');
cy.get('.chart-container').contains('No records found');
// Test column-specific search
cy.get('.search-select').should('be.visible');
cy.get('.search-select').click();
cy.get('.ant-select-dropdown').should('be.visible');
cy.get('.ant-select-item-option').contains('state').should('be.visible');
cy.get('.ant-select-item-option').contains('state').click();
cy.get(searchInputSelector).clear();
cy.get(searchInputSelector).type('CA');
cy.wait('@chartData');
cy.wait(1000);
cy.get('td[aria-labelledby="header-state"]').should('be.visible');
cy.get('td[aria-labelledby="header-state"]')
.first()
.should('contain', 'CA');
});
});

View File

@@ -22,7 +22,7 @@ describe('SqlLab query tabs', () => {
});
const tablistSelector = '[data-test="sql-editor-tabs"] > [role="tablist"]';
const tabSelector = `${tablistSelector} [role="tab"]:not([type="button"])`;
const tabSelector = `${tablistSelector} [role="tab"]`;
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('.ant-dropdown-menu')
cy.get('.antd5-dropdown-menu')
.last()
.find('.ant-dropdown-menu-item')
.find('.antd5-dropdown-menu-item')
.first()
.click({ force: true });

View File

@@ -25,16 +25,16 @@ export function dataTestChartName(chartName: string): string {
export const pageHeader = {
logo: '.navbar-brand > img',
headerNavigationItem: '.ant-menu-submenu-title',
headerNavigationItem: '.antd5-menu-submenu-title',
headerNavigationDropdown: "[aria-label='triangle-down']",
headerNavigationItemMenu: '.ant-menu-item-group-list',
plusIcon: ':nth-child(2) > .ant-menu-submenu-title',
headerNavigationItemMenu: '.antd5-menu-item-group-list',
plusIcon: ':nth-child(2) > .antd5-menu-submenu-title',
plusIconMenuOptions: {
sqlQueryOption: dataTestLocator('menu-item-SQL query'),
chartOption: dataTestLocator('menu-item-Chart'),
dashboardOption: dataTestLocator('menu-item-Dashboard'),
},
plusMenu: '.ant-menu-submenu-popup',
plusMenu: '.antd5-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: '.ant-badge-count',
rolesBubble: '.antd5-badge-count',
};
export const homePage = {
homeSection: {
sectionArea: '.ant-collapse-content-box',
sectionElement: '.ant-card-meta-title',
sectionElement: '.antd5-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: '.ant-alert',
infoAlert: '.antd5-alert',
serviceAccountInput: '[name="credentials_info"]',
connectionStep: {
modal: '.ant-modal-content',
modalBody: '.ant-modal-body',
modal: '.antd5-modal-content',
modalBody: '.antd5-modal-body',
stepTitle: '.css-7x6kk > h4',
helperBottom: '.helper-bottom',
postgresDatabase: '[name="database"]',
dbInput: '[name="database_name"]',
alertMessage: '.ant-alert-message',
alertMessage: '.antd5-alert-message',
errorField: '[role="alert"]',
uploadJson: '[title="Upload JSON file"]',
chooseFile: '[class="ant-btn input-upload-btn"]',
chooseFile: '[class="antd5-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: '.ant-tooltip-content',
tooltip: '.antd5-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 > .ant-btn',
saveAsButton: '.SaveQuery > .antd5-btn',
saveAsModal: {
footer: '.ant-modal-footer',
footer: '.antd5-modal-footer',
queryNameInput: 'input[class^="ant-input"]',
},
sqlToolbar: {
@@ -158,15 +158,15 @@ export const sqlLabView = {
runButton: '.css-d3dxop',
},
rowsLimit: {
dropdown: '.ant-dropdown-menu',
limitButton: '.ant-dropdown-menu-item',
dropdown: '.antd5-dropdown-menu',
limitButton: '.antd5-dropdown-menu-item',
limitButtonText: '.css-151uxnz',
limitTextWithValue: '[class="ant-dropdown-trigger"]',
limitTextWithValue: '[class="antd5-dropdown-trigger"]',
},
renderedTableHeader: '.ReactVirtualized__Table__headerRow',
renderedTableRow: '.ReactVirtualized__Table__row',
errorBody: '.error-body',
alertMessage: '.ant-alert-message',
alertMessage: '.antd5-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 > .ant-btn',
addAnnotationLayerButton: '.ant-empty-footer > .antd5-btn',
},
modal: {
content: {
content: '.ant-modal-body',
title: '.ant-modal-body > :nth-child(2) > input',
content: '.antd5-modal-body',
title: '.antd5-modal-body > :nth-child(2) > input',
description: "[name='descr']",
},
footer: {
footer: '.ant-modal-footer',
footer: '.antd5-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: '.ant-modal-body',
body: '.antd5-modal-body',
},
table: {
tableRow: {
@@ -261,7 +261,7 @@ export const datasetsList = {
},
},
deleteDatasetModal: {
modal: '.ant-modal-content',
modal: '.antd5-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="card-view"]',
listView: '[aria-label="list-view"]',
cardView: '[aria-label="appstore"]',
listView: '[aria-label="unordered-list"]',
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="ant-card-cover"]',
cardCover: '[class="antd5-card-cover"]',
cardImage: '[class="gradient-container"]',
starIcon: dataTestLocator('fave-unfave-icon'),
},
@@ -294,8 +294,8 @@ export const chartListView = {
},
table: {
bulkSelect: {
checkboxOff: 'input[type="checkbox"]:checked',
checkboxOn: 'input[type="checkbox"]:not(:checked)',
checkboxOff: '[aria-label="checkbox-off"]',
checkboxOn: '[aria-label="checkbox-on"]',
action: dataTestLocator('bulk-select-action'),
},
tableList: dataTestLocator('listview-table'),
@@ -316,14 +316,14 @@ export const chartListView = {
};
export const nativeFilters = {
modal: {
container: '.ant-modal',
footer: '.ant-modal-footer',
container: '.antd5-modal',
footer: '.antd5-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: '.ant-alert-message',
alertXUnsavedFilters: '.antd5-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="ant-card-cover"]',
cardCover: '[class="antd5-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 > .ant-btn > span',
selectFileButton: '.ant-upload > .antd5-btn > span',
importButton: dataTestLocator('modal-confirm-button'),
},
header: {
@@ -474,15 +474,15 @@ export const exploreView = {
},
chartAreaItem: '.nv-legend-text',
viewQueryModal: {
container: '.ant-modal-content',
closeButton: 'button.ant-modal-close',
container: '.antd5-modal-content',
closeButton: 'button.antd5-modal-close',
},
embedCodeModal: {
container: dataTestLocator('embed-code-popover'),
textfield: dataTestLocator('embed-code-textarea'),
},
saveModal: {
modal: '.ant-modal-content',
modal: '.antd5-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: '.ant-popover-content',
container: '.antd5-popover-content',
footer: '.footer',
cancelButton: dataTestLocator('cancel-button'),
configureLastTimeRange: {
@@ -578,15 +578,15 @@ export const exploreView = {
},
},
editDatasetModal: {
container: '.ant-modal-content',
container: '.antd5-modal-content',
datasetTabsContainer: dataTestLocator('edit-dataset-tabs'),
saveButton: dataTestLocator('datasource-modal-save'),
metricsTab: {
addItem: dataTestLocator('crud-add-table-item'),
rowsContainer: '.ant-table-body',
rowsContainer: dataTestLocator('table-content-rows'),
},
confirmModal: {
okButton: '.ant-modal-confirm-btns .ant-btn-primary',
okButton: '.antd5-modal-confirm-btns .antd5-btn-primary',
},
},
visualizationTypeModal: {
@@ -617,12 +617,12 @@ export const dashboardView = {
closeButton: dataTestLocator('close-button'),
},
saveModal: {
modal: '.ant-modal-content',
modal: '.antd5-modal-content',
dashboardNameInput: '.ant-input',
saveButton: dataTestLocator('modal-save-dashboard-button'),
},
dashboardProperties: {
modal: '.ant-modal-content',
modal: '.antd5-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 .ant-dropdown-trigger',
'.header-with-actions .right-button-panel .antd5-dropdown-trigger',
threeDotsMenuDropdown: dataTestLocator('header-actions-menu'),
refreshDashboard: dataTestLocator('refresh-dashboard-menu-item'),
saveAsMenuOption: dataTestLocator('save-as-menu-item'),

View File

@@ -69,21 +69,7 @@ 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();
@@ -91,9 +77,6 @@ before(() => {
});
beforeEach(() => {
if (skipLogin()) {
return;
}
cy.cleanDashboards();
cy.cleanCharts();
});

View File

@@ -143,29 +143,3 @@ 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,
});
};

View File

@@ -28,5 +28,3 @@ 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/';

View File

@@ -16,8 +16,10 @@
* 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?$',
@@ -28,9 +30,7 @@ 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/$2',
'^@superset-ui/([^/]+)$': '<rootDir>/node_modules/@superset-ui/$1/src',
'@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|geostyler|geostyler-.*)',
'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)',
],
preset: 'ts-jest',
transform: {

File diff suppressed because it is too large Load Diff

View File

@@ -37,7 +37,7 @@
"src/setup/*"
],
"scripts": {
"_prettier": "prettier './({src,spec,cypress-base,plugins,packages,.storybook}/**/*{.js,.jsx,.ts,.tsx,.css,.scss,.sass}|package.json)'",
"_prettier": "prettier './({src,spec,cypress-base,plugins,packages,.storybook}/**/*{.js,.jsx,.ts,.tsx,.css,.less,.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,10 +70,7 @@
"storybook": "cross-env NODE_ENV=development BABEL_ENV=development storybook dev -p 6006",
"tdd": "cross-env NODE_ENV=test NODE_OPTIONS=\"--max-old-space-size=8192\" jest --watch",
"test": "cross-env NODE_ENV=test NODE_OPTIONS=\"--max-old-space-size=8192\" jest --max-workers=80% --silent",
"test-loud": "cross-env NODE_ENV=test NODE_OPTIONS=\"--max-old-space-size=8192\" jest --max-workers=80%",
"type": "tsc --noEmit",
"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"
},
@@ -84,9 +81,12 @@
"last 3 edge versions"
],
"dependencies": {
"@ant-design/icons": "^5.2.6",
"@emotion/cache": "^11.4.0",
"@emotion/react": "^11.14.0",
"@emotion/react": "^11.13.3",
"@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,7 +115,6 @@
"@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",
@@ -123,11 +122,16 @@
"@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": "^5.24.6",
"antd": "4.10.3",
"antd-v5": "npm:antd@^5.18.0",
"bootstrap": "^3.4.1",
"brace": "^0.11.1",
"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",
@@ -140,14 +144,12 @@
"fs-extra": "^11.2.0",
"fuse.js": "^7.0.0",
"geolib": "^2.0.24",
"geostyler": "^14.1.3",
"geostyler": "^12.0.2",
"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",
@@ -171,15 +173,17 @@
"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-google-recaptcha": "^3.1.0",
"react-draggable": "^4.4.6",
"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",
@@ -193,12 +197,14 @@
"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",
@@ -224,7 +230,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.27.2",
"@babel/preset-env": "^7.26.7",
"@babel/preset-react": "^7.26.3",
"@babel/preset-typescript": "^7.26.0",
"@babel/register": "^7.23.7",
@@ -256,12 +262,12 @@
"@types/dom-to-image": "^2.6.7",
"@types/enzyme": "^3.10.18",
"@types/fetch-mock": "^7.3.2",
"@types/jest": "^29.5.14",
"@types/jest": "^29.5.12",
"@types/jquery": "^3.5.8",
"@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",
@@ -271,6 +277,7 @@
"@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",
@@ -279,14 +286,13 @@
"@types/redux-mock-store": "^1.0.6",
"@types/rison": "0.1.0",
"@types/sinon": "^17.0.3",
"@types/testing-library__jest-dom": "^5.14.9",
"@types/tinycolor2": "^1.4.3",
"@types/yargs": "12 - 18",
"@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0",
"@wojtekmaj/enzyme-adapter-react-17": "^0.8.0",
"babel-jest": "^29.7.0",
"babel-loader": "^10.0.0",
"babel-loader": "^9.1.3",
"babel-plugin-dynamic-import-node": "^2.3.3",
"babel-plugin-jsx-remove-data-test-id": "^3.0.0",
"babel-plugin-lodash": "^3.3.4",
@@ -298,12 +304,9 @@
"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",
@@ -327,13 +330,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",
@@ -351,10 +354,7 @@
"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,12 +365,6 @@
"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"
@@ -380,8 +374,7 @@
"d3-color": "^3.1.0",
"puppeteer": "^22.4.1",
"underscore": "^1.13.7",
"jspdf": "^3.0.1",
"nwsapi": "^2.2.13"
"jspdf": "^3.0.1"
},
"readme": "ERROR: No README data found!",
"scarfSettings": {

View File

@@ -4194,7 +4194,7 @@
"peerDependencies": {
"@emotion/react": "^11.4.1",
"@types/react": "*",
"antd": "^5.24.6",
"antd": "^4.9.4",
"react": "^16.13.1",
"react-dom": "^16.13.1"
}

View File

@@ -25,14 +25,29 @@ 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/theme/index.ts
// https://github.com/apache-superset/superset-ui/blob/master/packages/superset-ui-core/src/style/index.ts
const Styles = styled.div<<%= packageLabel %>StylesProps>`
background-color: ${({ theme }) => theme.colors.primary.light2};
padding: ${({ theme }) => theme.sizeUnit * 4}px;
border-radius: ${({ theme }) => theme.borderRadius}px;
background-color: ${({ theme }) => theme.colors.secondary.light2};
padding: ${({ theme }) => theme.gridUnit * 4}px;
border-radius: ${({ theme }) => theme.gridUnit * 2}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;
}
`;
/**

View File

@@ -40,6 +40,7 @@
"@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",

View File

@@ -18,7 +18,7 @@
*/
import { kebabCase } from 'lodash';
import { t, useTheme, styled } from '@superset-ui/core';
import { Tooltip } from '@superset-ui/core/components';
import Tooltip from './Tooltip';
interface CertifiedIconWithTooltipProps {
certifiedBy?: string | null;
@@ -27,7 +27,7 @@ interface CertifiedIconWithTooltipProps {
}
const StyledDiv = styled.div`
margin-bottom: ${({ theme }) => theme.sizeUnit * 2}px;
margin-bottom: ${({ theme }) => theme.gridUnit * 2}px;
`;
function CertifiedIconWithTooltip({
@@ -58,7 +58,7 @@ function CertifiedIconWithTooltip({
>
<g>
<path
fill={theme.colorPrimary}
fill={theme.colors.primary.base}
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>

View File

@@ -17,12 +17,8 @@
* under the License.
*/
import { useState, ReactNode, useLayoutEffect, RefObject } from 'react';
import { css, styled, SupersetTheme } from '@superset-ui/core';
import {
SafeMarkdown,
Tooltip,
InfoTooltip,
} from '@superset-ui/core/components';
import { css, SafeMarkdown, styled, SupersetTheme } from '@superset-ui/core';
import { Tooltip } from './Tooltip';
import { ColumnTypeLabel } from './ColumnTypeLabel/ColumnTypeLabel';
import CertifiedIconWithTooltip from './CertifiedIconWithTooltip';
import { ColumnMeta } from '../types';
@@ -32,6 +28,7 @@ import {
getColumnTypeTooltipNode,
} from './labelUtils';
import { SQLPopover } from './SQLPopover';
import InfoTooltipWithTrigger from './InfoTooltipWithTrigger';
export type ColumnOptionProps = {
column: ColumnMeta;
@@ -43,7 +40,7 @@ const StyleOverrides = styled.span`
display: flex;
align-items: center;
svg {
margin-right: ${({ theme }) => theme.sizeUnit}px;
margin-right: ${({ theme }) => theme.gridUnit}px;
}
`;
@@ -85,7 +82,7 @@ export function ColumnOption({
<span
className="option-label column-option-label"
css={(theme: SupersetTheme) => css`
margin-right: ${theme.sizeUnit}px;
margin-right: ${theme.gridUnit}px;
`}
ref={labelRef}
>
@@ -101,13 +98,15 @@ export function ColumnOption({
/>
)}
{warningMarkdown && (
<InfoTooltip
type="warning"
<InfoTooltipWithTrigger
className="text-warning"
icon="warning"
tooltip={<SafeMarkdown source={warningMarkdown} />}
label={`warn-${column.column_name}`}
iconStyle={{ marginLeft: 0 }}
iconsStyle={{ marginLeft: 0 }}
{...(column.error_text && {
type: 'error',
className: 'text-danger',
icon: 'exclamation-circle',
})}
/>
)}

View File

@@ -39,9 +39,9 @@ const TypeIconWrapper = styled.div`
display: flex;
justify-content: center;
align-items: center;
width: ${theme.sizeUnit * 6}px;
height: ${theme.sizeUnit * 6}px;
margin-right: ${theme.sizeUnit}px;
width: ${theme.gridUnit * 6}px;
height: ${theme.gridUnit * 6}px;
margin-right: ${theme.gridUnit}px;
&& svg {
margin-right: 0;

View File

@@ -17,9 +17,9 @@
* under the License.
*/
import { ReactNode } from 'react';
import { t, css } from '@superset-ui/core';
import { InfoCircleOutlined } from '@ant-design/icons';
import { InfoTooltip, Tooltip } from '@superset-ui/core/components';
import { t } from '@superset-ui/core';
import { InfoTooltipWithTrigger } from './InfoTooltipWithTrigger';
import { Tooltip } from './Tooltip';
type ValidationError = string;
@@ -60,7 +60,7 @@ export function ControlHeader({
<span>
{description && (
<span>
<InfoTooltip
<InfoTooltipWithTrigger
label={t('description')}
tooltip={description}
placement="top"
@@ -70,11 +70,11 @@ export function ControlHeader({
)}
{renderTrigger && (
<span>
<InfoTooltip
<InfoTooltipWithTrigger
label={t('bolt')}
tooltip={t('Changing this control takes effect instantly')}
placement="top"
type="notice"
icon="bolt"
/>{' '}
</span>
)}
@@ -88,7 +88,6 @@ export function ControlHeader({
return null;
}
const labelClass = validationErrors.length > 0 ? 'text-danger' : '';
return (
<div className="ControlHeader" data-test={`${name}-header`}>
<div className="pull-left">
@@ -99,30 +98,25 @@ 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}>
<InfoCircleOutlined
css={theme => css`
font-size: ${theme.sizeUnit * 3}px;
color: ${theme.colorError};
`}
/>
{/* TODO: Remove fa-icon */}
{/* eslint-disable-next-line icons/no-fa-icons-usage */}
<i className="fa fa-exclamation-circle text-warning" />
</Tooltip>{' '}
</span>
)}
{danger && (
<span>
<Tooltip id="error-tooltip" placement="top" title={danger}>
<InfoCircleOutlined
css={theme => css`
font-size: ${theme.sizeUnit * 3}px;
color: ${theme.colorError};
`}
/>{' '}
{/* TODO: Remove fa-icon */}
{/* eslint-disable-next-line icons/no-fa-icons-usage */}
<i className="fa fa-exclamation-circle text-danger" />
</Tooltip>{' '}
</span>
)}
@@ -133,17 +127,18 @@ export function ControlHeader({
placement="top"
title={validationErrors.join(' ')}
>
<InfoCircleOutlined
css={theme => css`
font-size: ${theme.sizeUnit * 3}px;
color: ${theme.colorError};
`}
/>{' '}
{/* TODO: Remove fa-icon */}
{/* eslint-disable-next-line icons/no-fa-icons-usage */}
<i className="fa fa-exclamation-circle text-danger" />
</Tooltip>{' '}
</span>
)}
{renderOptionalIcons()}
{required && <strong> *</strong>}
{required && (
<span className="text-danger m-l-4">
<strong>*</strong>
</span>
)}
</label>
</div>
{rightNode && <div className="pull-right">{rightNode}</div>}

View File

@@ -20,8 +20,9 @@ import { styled, css } from '@superset-ui/core';
export const ControlSubSectionHeader = styled.div`
${({ theme }) => css`
font-weight: ${theme.fontWeightStrong};
margin-bottom: ${theme.sizeUnit}px;
font-size: ${theme.fontSizeSM}px;
font-weight: ${theme.typography.weights.bold};
font-size: ${theme.typography.sizes.s};
margin-bottom: ${theme.gridUnit}px;
`}
`;
export default ControlSubSectionHeader;

View File

@@ -17,4 +17,5 @@
* under the License.
*/
export { Dropdown, type DropdownProps } from '@superset-ui/core/components';
export { Dropdown } from 'antd';
export type { DropDownProps } from 'antd/lib/dropdown';

View File

@@ -0,0 +1,80 @@
/**
* 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;

View File

@@ -17,4 +17,5 @@
* under the License.
*/
export { Menu, type MenuProps } from '@superset-ui/core/components';
export { Menu } from 'antd';
export type { MenuProps } from 'antd/lib/menu';

View File

@@ -18,16 +18,17 @@
*/
import { useState, ReactNode, useLayoutEffect, RefObject } from 'react';
import { css, styled, Metric, SupersetTheme } from '@superset-ui/core';
import {
css,
styled,
Metric,
SafeMarkdown,
Typography,
// TODO: somehow doesn't work with our main Tooltip (?)
RawAntdTooltip as Tooltip,
InfoTooltip,
} from '@superset-ui/core/components';
SupersetTheme,
} from '@superset-ui/core';
import InfoTooltipWithTrigger from './InfoTooltipWithTrigger';
import { ColumnTypeLabel } from './ColumnTypeLabel/ColumnTypeLabel';
import CertifiedIconWithTooltip from './CertifiedIconWithTooltip';
import Tooltip from './Tooltip';
import { getMetricTooltipNode } from './labelUtils';
import { SQLPopover } from './SQLPopover';
@@ -36,7 +37,7 @@ const FlexRowContainer = styled.div`
display: flex;
> svg {
margin-right: ${({ theme }) => theme.sizeUnit}px;
margin-right: ${({ theme }) => theme.gridUnit}px;
}
`;
@@ -60,26 +61,23 @@ 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.sizeUnit}px;
margin-right: ${theme.gridUnit}px;
`}
ref={labelRef}
>
{url ? (
<Typography.Link
href={url}
target={openInNewWindow ? '_blank' : ''}
rel="noreferrer"
>
{verbose}
</Typography.Link>
) : (
verbose
)}
{link}
</span>
);
@@ -113,13 +111,15 @@ export function MetricOption({
/>
)}
{warningMarkdown && (
<InfoTooltip
type="warning"
<InfoTooltipWithTrigger
className="text-warning"
icon="warning"
tooltip={<SafeMarkdown source={warningMarkdown} />}
label={`warn-${metric.metric_name}`}
iconStyle={{ marginLeft: 0 }}
iconsStyle={{ marginLeft: 0 }}
{...(metric.error_text && {
type: 'error',
className: 'text-danger',
icon: 'exclamation-circle',
})}
/>
)}

View File

@@ -17,18 +17,19 @@
* under the License.
*/
import { useEffect, useState } from 'react';
import { Popover, type PopoverProps } from '@superset-ui/core/components';
import { Popover } from 'antd-v5';
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.fontSizeSM}px;
font-size: ${theme.typography.sizes.s}px;
& svg {
margin-left: ${theme.sizeUnit}px;
margin-right: ${theme.sizeUnit}px;
margin-left: ${theme.gridUnit}px;
margin-right: ${theme.gridUnit}px;
}
`}
`;
@@ -64,9 +65,9 @@ export const SQLPopover = (props: PopoverProps & { sqlExpression: string }) => {
readOnly
wrapEnabled
style={{
border: `1px solid ${theme.colorBorder}`,
background: theme.colorPrimaryBg,
maxWidth: theme.sizeUnit * 100,
border: `1px solid ${theme.colors.grayscale.light2}`,
background: theme.colors.secondary.light5,
maxWidth: theme.gridUnit * 100,
}}
/>
}

View File

@@ -17,10 +17,7 @@
* under the License.
*/
import { useState, ReactNode } from 'react';
import {
RawAntdSelect as AntdSelect,
type RawAntdSelectProps as AntdSelectProps,
} from '@superset-ui/core/components';
import AntdSelect, { SelectProps as AntdSelectProps } from 'antd/lib/select';
export const { Option }: any = AntdSelect;
@@ -38,7 +35,7 @@ export type SelectProps<VT> = Omit<AntdSelectProps<VT>, 'options'> & {
export default function Select<VT extends string | number>({
creatable,
onSearch,
popupMatchSelectWidth = false,
dropdownMatchSelectWidth = false,
minWidth = '100%',
showSearch: showSearch_ = true,
onChange,
@@ -76,7 +73,7 @@ export default function Select<VT extends string | number>({
return (
<AntdSelect<VT>
popupMatchSelectWidth={popupMatchSelectWidth}
dropdownMatchSelectWidth={dropdownMatchSelectWidth}
showSearch={showSearch}
onSearch={handleSearch}
onChange={handleChange}

View File

@@ -0,0 +1,60 @@
/*
* 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;

View File

@@ -29,18 +29,18 @@ const TooltipSectionWrapper = styled.div`
overflow: hidden;
text-overflow: ellipsis;
font-size: ${theme.fontSizeSM}px;
font-size: ${theme.typography.sizes.s}px;
line-height: 1.2;
&:not(:last-of-type) {
margin-bottom: ${theme.sizeUnit * 2}px;
margin-bottom: ${theme.gridUnit * 2}px;
}
`}
`;
const TooltipSectionLabel = styled.span`
${({ theme }) => css`
font-weight: ${theme.fontWeightStrong};
font-weight: ${theme.typography.weights.bold};
`}
`;

View File

@@ -26,7 +26,6 @@ import {
import { ColumnMeta, SortSeriesData, SortSeriesType } from './types';
export const DEFAULT_MAX_ROW = 100000;
export const DEFAULT_MAX_ROW_TABLE_SERVER = 500000;
// eslint-disable-next-line import/prefer-default-export
export const TIME_FILTER_LABELS = {

View File

@@ -25,12 +25,14 @@ 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';

View File

@@ -30,7 +30,7 @@ export const aggregationOperator: PostProcessingFactory<
> = (formData: QueryFormData, queryObject) => {
const { aggregation = 'LAST_VALUE' } = formData;
if (aggregation === 'LAST_VALUE' || aggregation === 'raw') {
if (aggregation === 'LAST_VALUE') {
return undefined;
}

View File

@@ -17,8 +17,7 @@
* under the License.
*/
import { ReactNode } from 'react';
import { JsonValue } from '@superset-ui/core';
import { Radio } from '@superset-ui/core/components';
import { JsonValue, useTheme } from '@superset-ui/core';
import { ControlHeader } from '../../components/ControlHeader';
// [value, label]
@@ -43,19 +42,51 @@ export default function RadioButtonControl({
...props
}: RadioButtonControlProps) {
const currentValue = initialValue || options[0][0];
const theme = useTheme();
return (
<div>
<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',
},
}}
>
<ControlHeader {...props} />
<Radio.Group
value={currentValue}
onChange={e => onChange(e.target.value)}
>
<div className="btn-group btn-group-sm">
{options.map(([val, label]) => (
<Radio.Button key={JSON.stringify(val)} value={val}>
<button
key={JSON.stringify(val)}
type="button"
className={`btn btn-default ${
val === currentValue ? 'active' : ''
}`}
onClick={() => {
onChange(val);
}}
>
{label}
</Radio.Button>
</button>
))}
</Radio.Group>
</div>
</div>
);
}

View File

@@ -70,7 +70,6 @@ export const aggregationControl = {
clearable: false,
renderTrigger: false,
choices: [
['raw', t('None')],
['LAST_VALUE', t('Last Value')],
['sum', t('Total (Sum)')],
['mean', t('Average (Mean)')],
@@ -78,9 +77,7 @@ export const aggregationControl = {
['max', t('Maximum')],
['median', t('Median')],
],
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.',
),
description: t('Select an aggregation method to apply to the metric.'),
provideFormDataToProps: true,
mapStateToProps: ({ form_data }: ControlPanelState) => ({
value: form_data.aggregation || 'LAST_VALUE',

View File

@@ -300,7 +300,7 @@ export interface FilterOption<T extends SelectOption> {
data: T;
}
// Ref: superset-frontend/@superset-ui/core/components/Select/SupersetStyledSelect.tsx
// Ref: superset-frontend/src/components/Select/SupersetStyledSelect.tsx
export interface SelectControlConfig<
O extends SelectOption = SelectOption,
T extends SelectControlType = SelectControlType,
@@ -371,9 +371,7 @@ 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;

View File

@@ -17,7 +17,7 @@
* under the License.
*/
import '@testing-library/jest-dom';
import { render } from '@superset-ui/core/spec';
import { render } from '@testing-library/react';
import {
ThemeProvider,
supersetTheme,
@@ -26,21 +26,17 @@ import {
import { ColumnOption, ColumnOptionProps } from '../../src';
jest.mock('@superset-ui/chart-controls/components/SQLPopover', () => ({
jest.mock('../../src/components/SQLPopover', () => ({
SQLPopover: () => <div data-test="mock-sql-popover" />,
}));
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/ColumnTypeLabel/ColumnTypeLabel', () => ({
ColumnTypeLabel: ({ type }: { type: string }) => (
<div data-test="mock-column-type-label">{type}</div>
),
}));
jest.mock('../../src/components/InfoTooltipWithTrigger', () => () => (
<div data-test="mock-info-tooltip-with-trigger" />
));
const defaultProps: ColumnOptionProps = {
column: {
@@ -118,11 +114,11 @@ test('dttm column has correct column label if showType is true', () => {
String(GenericDataType.Temporal),
);
});
test('doesnt show InfoTooltip when no warning', () => {
test('doesnt show InfoTooltipWithTrigger when no warning', () => {
const { queryByText } = setup();
expect(queryByText('mock-tooltip')).not.toBeInTheDocument();
expect(queryByText('mock-info-tooltip-with-trigger')).not.toBeInTheDocument();
});
test('shows a warning with InfoTooltip when it contains warning', () => {
test('shows a warning with InfoTooltipWithTrigger when it contains warning', () => {
const { getByTestId } = setup({
...defaultProps,
column: {
@@ -130,5 +126,5 @@ test('shows a warning with InfoTooltip when it contains warning', () => {
warning_text: 'This is a warning',
},
});
expect(getByTestId('mock-tooltip')).toBeInTheDocument();
expect(getByTestId('mock-info-tooltip-with-trigger')).toBeInTheDocument();
});

View File

@@ -17,7 +17,7 @@
* under the License.
*/
import { isValidElement } from 'react';
import { render, screen } from '@superset-ui/core/spec';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import { GenericDataType } from '@superset-ui/core';

View File

@@ -17,11 +17,11 @@
* under the License.
*/
import '@testing-library/jest-dom';
import { fireEvent, render } from '@superset-ui/core/spec';
import { fireEvent, render } from '@testing-library/react';
import { ThemeProvider, supersetTheme } from '@superset-ui/core';
import { InfoTooltip, InfoTooltipProps } from '@superset-ui/core/components';
import { InfoTooltipWithTrigger, InfoTooltipWithTriggerProps } from '../../src';
jest.mock('@superset-ui/core/components/Tooltip', () => ({
jest.mock('../../src/components/Tooltip', () => ({
Tooltip: ({ children }: { children: React.ReactNode }) => (
<div data-test="mock-tooltip">{children}</div>
),
@@ -29,10 +29,10 @@ jest.mock('@superset-ui/core/components/Tooltip', () => ({
const defaultProps = {};
const setup = (props: Partial<InfoTooltipProps> = {}) =>
const setup = (props: Partial<InfoTooltipWithTriggerProps> = {}) =>
render(
<ThemeProvider theme={supersetTheme}>
<InfoTooltip {...defaultProps} {...props} />
<InfoTooltipWithTrigger {...defaultProps} {...props} />
</ThemeProvider>,
);
@@ -44,29 +44,31 @@ test('renders a tooltip', () => {
expect(getAllByTestId('mock-tooltip').length).toEqual(1);
});
test('responds to keydown events', () => {
test('renders an info icon', () => {
const { container } = setup();
expect(container.getElementsByClassName('fa-info-circle')).toHaveLength(1);
});
test('responds to keypresses', () => {
const clickHandler = jest.fn();
const { getByRole } = setup({
label: 'test',
tooltip: 'this is a test',
onClick: clickHandler,
});
fireEvent.keyDown(getByRole('button'), {
fireEvent.keyPress(getByRole('button'), {
key: 'Tab',
code: 9,
charCode: 9,
});
expect(clickHandler).toHaveBeenCalledTimes(0);
fireEvent.keyDown(getByRole('button'), {
fireEvent.keyPress(getByRole('button'), {
key: 'Enter',
code: 13,
charCode: 13,
});
expect(clickHandler).toHaveBeenCalledTimes(1);
fireEvent.keyDown(getByRole('button'), {
fireEvent.keyPress(getByRole('button'), {
key: ' ',
code: 32,
charCode: 32,
@@ -74,47 +76,9 @@ test('responds to keydown events', () => {
expect(clickHandler).toHaveBeenCalledTimes(2);
});
test('finds the info circle icon inside info variant', () => {
test('has a bsStyle', () => {
const { container } = setup({
type: 'info',
bsStyle: 'something',
});
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();
expect(container.getElementsByClassName('text-something')).toHaveLength(1);
});

View File

@@ -17,31 +17,24 @@
* under the License.
*/
import '@testing-library/jest-dom';
import { render } from '@superset-ui/core/spec';
import { render } from '@testing-library/react';
import { ThemeProvider, supersetTheme } from '@superset-ui/core';
import {
MetricOption,
MetricOptionProps,
} from '../../src/components/MetricOption';
import { MetricOption, MetricOptionProps } from '../../src';
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" />
));
jest.mock('../../src/components/ColumnTypeLabel/ColumnTypeLabel', () => ({
ColumnTypeLabel: () => <div data-test="mock-column-type-label" />,
}));
jest.mock(
'@superset-ui/chart-controls/components/ColumnTypeLabel/ColumnTypeLabel',
() => ({
ColumnTypeLabel: () => <div data-test="mock-column-type-label" />,
}),
);
jest.mock(
'@superset-ui/core/components/Tooltip',
'../../src/components/Tooltip',
() =>
({ children }: { children: React.ReactNode }) => (
<div data-test="mock-tooltip">{children}</div>
),
);
jest.mock('@superset-ui/chart-controls/components/SQLPopover', () => ({
jest.mock('../../src/components/SQLPopover', () => ({
SQLPopover: () => <div data-test="mock-sql-popover" />,
}));
@@ -72,9 +65,9 @@ test('shows a label with verbose_name', () => {
expect(lbl).toHaveLength(1);
expect(`${lbl[0].textContent}`).toEqual(defaultProps.metric.verbose_name);
});
test('shows a InfoTooltip', () => {
test('shows a InfoTooltipWithTrigger', () => {
const { getByTestId } = setup();
expect(getByTestId('mock-tooltip')).toBeInTheDocument();
expect(getByTestId('mock-info-tooltip-with-trigger')).toBeInTheDocument();
});
test('shows SQL Popover trigger', () => {
const { getByTestId } = setup();
@@ -89,14 +82,14 @@ test('shows a label with metric_name when no verbose_name', () => {
});
expect(getByText(defaultProps.metric.metric_name)).toBeInTheDocument();
});
test('doesnt show InfoTooltip when no warning', () => {
test('doesnt show InfoTooltipWithTrigger when no warning', () => {
const { queryByText } = setup({
metric: {
...defaultProps.metric,
warning_text: '',
},
});
expect(queryByText('mock-tooltip')).not.toBeInTheDocument();
expect(queryByText('mock-info-tooltip-with-trigger')).not.toBeInTheDocument();
});
test('sets target="_blank" when openInNewWindow is true', () => {
const { getByRole } = setup({

View File

@@ -17,7 +17,7 @@
* under the License.
*/
import { ReactElement } from 'react';
import { render, screen } from '@superset-ui/core/spec';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import { ThemeProvider, supersetTheme } from '@superset-ui/core';
import {

View File

@@ -129,7 +129,7 @@ test('returns empty array if timeseries_limit_metric is an empty array', () => {
expect(
extractExtraMetrics({
...baseFormData,
// @ts-expect-error TS(2322): Type 'never[]' is not assignable to type 'QueryFor... Remove this comment to see the full error message
// @ts-ignore
timeseries_limit_metric: [],
}),
).toEqual([]);

View File

@@ -51,7 +51,7 @@ describe('defineSavedMetrics', () => {
uuid: '1',
},
]);
// @ts-expect-error TS(2322): Type 'undefined' is not assignable to type 'Metric... Remove this comment to see the full error message
// @ts-ignore
expect(defineSavedMetrics({ ...dataset, metrics: undefined })).toEqual([]);
});

View File

@@ -284,7 +284,7 @@ describe('getColorFunction()', () => {
it('getColorFunction unsupported operator', () => {
const colorFunction = getColorFunction(
{
// @ts-expect-error TS(2322): Type '"unsupported operator"' is not assignable to... Remove this comment to see the full error message
// @ts-ignore
operator: 'unsupported operator',
targetValue: 50,
colorScheme: '#FF0000',

View File

@@ -1,68 +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.
*/
{
"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"
}
}

View File

@@ -24,37 +24,21 @@
"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",
@@ -72,10 +56,8 @@
"@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",
@@ -88,7 +70,6 @@
"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",
@@ -99,7 +80,6 @@
"@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",
@@ -108,27 +88,5 @@
},
"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"
}
}
}

View File

@@ -16,8 +16,8 @@
* specific language governing permissions and limitations
* under the License.
*/
import { CSSProperties, ReactNode } from 'react';
import { Table, type TableColumnsType } from 'antd';
import { CSSProperties, PureComponent, ReactNode } from 'react';
interface TooltipRowData {
key: string | number;
@@ -27,52 +27,43 @@ interface TooltipRowData {
valueStyle?: CSSProperties;
}
interface TooltipTableProps {
const defaultProps = {
className: '',
data: [] as TooltipRowData[],
};
type Props = {
className?: string;
data: TooltipRowData[];
}
} & Readonly<typeof defaultProps>;
const VALUE_CELL_STYLE: CSSProperties = { paddingLeft: 8, textAlign: 'right' };
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>
),
},
];
export default class TooltipTable extends PureComponent<Props, {}> {
static defaultProps = defaultProps;
return (
<Table
className={className}
columns={columns}
dataSource={data}
pagination={false}
showHeader={false}
bordered={false}
/>
);
};
render() {
const { className, data } = this.props;
export default TooltipTable;
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>
);
}
}

View File

@@ -18,7 +18,7 @@
*/
import { t } from '@superset-ui/core';
import { SupersetTheme } from '../..';
import { SupersetTheme } from '../../style';
import { FallbackPropsWithDimension } from './SuperChart';
export type Props = FallbackPropsWithDimension;

View File

@@ -18,7 +18,7 @@
*/
import { CSSProperties } from 'react';
import { css, styled } from '../../theme';
import { css, styled } from '../../style';
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.sizeUnit * 4}px;
padding: ${theme.gridUnit * 4}px;
& .no-results-title {
font-size: ${theme.fontSizeLG}px;
font-weight: ${theme.fontWeightStrong};
padding-bottom: ${theme.sizeUnit * 2};
font-size: ${theme.typography.sizes.l}px;
font-weight: ${theme.typography.weights.bold};
padding-bottom: ${theme.gridUnit * 2};
}
& .no-results-body {
font-size: ${theme.fontSize}px;
font-size: ${theme.typography.sizes.m}px;
}
`}
`;

View File

@@ -18,7 +18,7 @@
*/
// eslint-disable-next-line no-restricted-syntax -- whole React import is required for `reactify.test.tsx` Jest test passing.
import { Component, ComponentClass, WeakValidationMap } from 'react';
import React, { 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

View File

@@ -49,7 +49,6 @@ 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.
@@ -93,8 +92,6 @@ export default class ChartMetadata {
queryObjectCount: number;
dynamicQueryObjectCount: boolean;
parseMethod: ParseMethod;
suppressContextMenu?: boolean;
@@ -118,7 +115,6 @@ export default class ChartMetadata {
label = null,
labelExplanation = null,
queryObjectCount = 1,
dynamicQueryObjectCount = false,
parseMethod = 'json-bigint',
suppressContextMenu = false,
} = config;
@@ -149,7 +145,6 @@ export default class ChartMetadata {
this.label = label;
this.labelExplanation = labelExplanation;
this.queryObjectCount = queryObjectCount;
this.dynamicQueryObjectCount = dynamicQueryObjectCount;
this.parseMethod = parseMethod;
this.suppressContextMenu = suppressContextMenu;
}

View File

@@ -37,7 +37,7 @@ import {
SetDataMaskHook,
} from '../types/Base';
import { QueryData, DataRecordFilters } from '..';
import { SupersetTheme } from '../../theme';
import { SupersetTheme } from '../../style';
// TODO: more specific typing for these fields of ChartProps
type AnnotationData = PlainObject;

View File

@@ -1,24 +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 type { PropsWithChildren } from 'react';
import type { AlertProps as AntdAlertProps } from 'antd/es/alert';
export type AlertProps = PropsWithChildren<
Omit<AntdAlertProps, 'children'> & { roomBelow?: boolean }
>;

View File

@@ -1,33 +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 { getTooltipHTML } from './Tooltip';
test('getTooltipHTML returns the expected HTML (string inputs)', () => {
const html = getTooltipHTML({
title: 'tooltip title',
body: 'body text',
footer: 'footer note',
});
expect(html).toContain('tooltip-detail');
expect(html).toContain('tooltip title');
expect(html).toContain('body text');
expect(html).toContain('footer note');
});

Some files were not shown because too many files have changed in this diff Show More