Compare commits

..

4 Commits

Author SHA1 Message Date
Beto Dealmeida
7ab6953f1a Small improvements 2025-09-10 10:51:59 -04:00
Beto Dealmeida
b55cb206a5 Update schema 2025-09-08 12:39:59 -04:00
Beto Dealmeida
4e52bc8a0b Update frontend 2025-09-03 15:57:01 -04:00
Beto Dealmeida
7f55d3ff30 feat: use default catalog/schema on DB selector 2025-09-03 14:36:31 -04:00
160 changed files with 1971 additions and 4420 deletions

View File

@@ -32,7 +32,7 @@ jobs:
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
persist-credentials: true
ref: master

View File

@@ -31,7 +31,7 @@ jobs:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
if: steps.check_queued.outputs.count >= 20
uses: actions/checkout@v5
uses: actions/checkout@v4
- name: Cancel duplicate workflow runs
if: steps.check_queued.outputs.count >= 20

View File

@@ -18,7 +18,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
persist-credentials: false
submodules: recursive

View File

@@ -25,7 +25,7 @@ jobs:
pull-requests: write
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v5
uses: actions/checkout@v4
- name: Check and notify
uses: actions/github-script@v7
with:

View File

@@ -71,7 +71,7 @@ jobs:
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
fetch-depth: 1

View File

@@ -31,7 +31,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v4
- name: Check for file changes
id: check

View File

@@ -27,7 +27,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: "Checkout Repository"
uses: actions/checkout@v5
uses: actions/checkout@v4
- name: "Dependency Review"
uses: actions/dependency-review-action@v4
continue-on-error: true
@@ -53,7 +53,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: "Checkout Repository"
uses: actions/checkout@v5
uses: actions/checkout@v4
- name: Setup Python
uses: ./.github/actions/setup-backend/

View File

@@ -42,7 +42,7 @@ jobs:
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
persist-credentials: false
@@ -117,7 +117,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Check for file changes

View File

@@ -28,7 +28,7 @@ jobs:
run:
working-directory: superset-embedded-sdk
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: './superset-embedded-sdk/.nvmrc'

View File

@@ -18,7 +18,7 @@ jobs:
run:
working-directory: superset-embedded-sdk
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: './superset-embedded-sdk/.nvmrc'

View File

@@ -160,7 +160,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: "Checkout ${{ github.ref }} ( ${{ needs.ephemeral-env-label.outputs.sha }} : ${{steps.get-sha.outputs.sha}} )"
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
ref: ${{ needs.ephemeral-env-label.outputs.sha }}
persist-credentials: false
@@ -220,7 +220,7 @@ jobs:
pull-requests: write
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
with:
persist-credentials: false

View File

@@ -27,12 +27,12 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
persist-credentials: false
submodules: recursive
- name: Setup Java
uses: actions/setup-java@v5
uses: actions/setup-java@v4
with:
distribution: "temurin"
java-version: "11"

View File

@@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout Repository
uses: actions/checkout@v5
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4

View File

@@ -17,7 +17,7 @@ jobs:
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
persist-credentials: false

View File

@@ -12,7 +12,7 @@ jobs:
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
persist-credentials: false
submodules: recursive

View File

@@ -15,12 +15,12 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
persist-credentials: false
submodules: recursive
- name: Setup Java
uses: actions/setup-java@v5
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '11'

View File

@@ -16,7 +16,7 @@ jobs:
pull-requests: write
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
persist-credentials: false
submodules: recursive

View File

@@ -21,7 +21,7 @@ jobs:
python-version: ["current", "previous", "next"]
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
persist-credentials: false
submodules: recursive

View File

@@ -27,7 +27,7 @@ jobs:
pull-requests: write
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
persist-credentials: false
submodules: recursive

View File

@@ -26,7 +26,7 @@ jobs:
name: Bump version and publish package(s)
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
with:
# pulls all commits (needed for lerna / semantic release to correctly version)
fetch-depth: 0

View File

@@ -147,7 +147,7 @@ jobs:
- name: Checkout PR code (only if build needed)
if: steps.auth.outputs.authorized == 'true' && steps.check.outputs.build_needed == 'true'
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
ref: ${{ steps.check.outputs.target_sha }}
persist-credentials: false

View File

@@ -37,7 +37,7 @@ jobs:
- 16379:6379
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
persist-credentials: false
submodules: recursive

View File

@@ -51,7 +51,7 @@ jobs:
- 16379:6379
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
persist-credentials: false
submodules: recursive

View File

@@ -30,7 +30,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
persist-credentials: false
submodules: recursive

View File

@@ -31,7 +31,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
persist-credentials: false
submodules: recursive
@@ -41,7 +41,7 @@ jobs:
node-version-file: './docs/.nvmrc'
- name: Setup Python
uses: ./.github/actions/setup-backend/
- uses: actions/setup-java@v5
- uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: '21'

View File

@@ -18,7 +18,7 @@ jobs:
name: Link Checking
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
# Do not bump this linkinator-action version without opening
# an ASF Infra ticket to allow the new version first!
- uses: JustinBeckwith/linkinator-action@v1.11.0
@@ -56,7 +56,7 @@ jobs:
working-directory: docs
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
persist-credentials: false
submodules: recursive

View File

@@ -69,21 +69,21 @@ jobs:
# Conditional checkout based on context
- name: Checkout for push or pull_request event
if: github.event_name == 'push' || github.event_name == 'pull_request'
uses: actions/checkout@v5
uses: actions/checkout@v4
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@v5
uses: actions/checkout@v4
with:
persist-credentials: false
ref: ${{ github.event.inputs.ref }}
submodules: recursive
- name: Checkout using PR ID (workflow_dispatch)
if: github.event_name == 'workflow_dispatch' && github.event.inputs.pr_id != ''
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
persist-credentials: false
ref: refs/pull/${{ github.event.inputs.pr_id }}/merge

View File

@@ -24,7 +24,7 @@ jobs:
working-directory: superset-extensions-cli
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
persist-credentials: false
submodules: recursive
@@ -49,7 +49,7 @@ jobs:
- name: Upload coverage reports to Codecov
if: steps.check.outputs.superset-extensions-cli
uses: codecov/codecov-action@v5
uses: codecov/codecov-action@v3
with:
file: ./coverage.xml
flags: superset-extensions-cli

View File

@@ -23,7 +23,7 @@ jobs:
should-run: ${{ steps.check.outputs.frontend }}
steps:
- name: Checkout Code
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
persist-credentials: false
fetch-depth: 0
@@ -73,7 +73,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Download Docker Image Artifact
uses: actions/download-artifact@v5
uses: actions/download-artifact@v4
with:
name: docker-image
@@ -101,7 +101,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Download Coverage Artifacts
uses: actions/download-artifact@v5
uses: actions/download-artifact@v4
with:
pattern: coverage-artifacts-*
path: coverage/
@@ -127,7 +127,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Download Docker Image Artifact
uses: actions/download-artifact@v5
uses: actions/download-artifact@v4
with:
name: docker-image
@@ -151,7 +151,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Download Docker Image Artifact
uses: actions/download-artifact@v5
uses: actions/download-artifact@v4
with:
name: docker-image

View File

@@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
persist-credentials: false
submodules: recursive

View File

@@ -29,7 +29,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
ref: ${{ inputs.ref || github.ref_name }}
persist-credentials: true

View File

@@ -41,7 +41,7 @@ jobs:
- 16379:6379
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
persist-credentials: false
submodules: recursive
@@ -99,7 +99,7 @@ jobs:
- 16379:6379
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
persist-credentials: false
submodules: recursive
@@ -152,7 +152,7 @@ jobs:
- 16379:6379
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
persist-credentials: false
submodules: recursive

View File

@@ -48,7 +48,7 @@ jobs:
- 16379:6379
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
persist-credentials: false
submodules: recursive
@@ -108,7 +108,7 @@ jobs:
- 16379:6379
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
persist-credentials: false
submodules: recursive

View File

@@ -24,7 +24,7 @@ jobs:
PYTHONPATH: ${{ github.workspace }}
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
persist-credentials: false
submodules: recursive

View File

@@ -18,7 +18,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
persist-credentials: false
submodules: recursive
@@ -49,7 +49,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
persist-credentials: false
submodules: recursive

View File

@@ -21,7 +21,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Install dependencies

View File

@@ -38,7 +38,7 @@ jobs:
});
- name: "Checkout ( ${{ github.sha }} )"
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
persist-credentials: false

View File

@@ -47,7 +47,7 @@ jobs:
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
fetch-depth: 0
@@ -107,7 +107,7 @@ jobs:
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
fetch-depth: 0

View File

@@ -27,7 +27,7 @@ jobs:
name: Generate Reports
steps:
- name: Checkout Repository
uses: actions/checkout@v5
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4

View File

@@ -12,7 +12,7 @@ jobs:
steps:
- name: Welcome Message
uses: actions/first-interaction@v3
uses: actions/first-interaction@v2
continue-on-error: true
with:
repo-token: ${{ github.token }}

View File

@@ -65,7 +65,7 @@
"storybook": "^8.6.11",
"swagger-ui-react": "^5.27.1",
"tinycolor2": "^1.4.2",
"ts-loader": "^9.5.4"
"ts-loader": "^9.5.2"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "^3.8.1",

View File

@@ -13139,10 +13139,10 @@ ts-dedent@^2.0.0, ts-dedent@^2.2.0:
resolved "https://registry.yarnpkg.com/ts-dedent/-/ts-dedent-2.2.0.tgz#39e4bd297cd036292ae2394eb3412be63f563bb5"
integrity sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==
ts-loader@^9.5.4:
version "9.5.4"
resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.5.4.tgz#44b571165c10fb5a90744aa5b7e119233c4f4585"
integrity sha512-nCz0rEwunlTZiy6rXFByQU1kVVpCIgUpc/psFiKVrUwrizdnIbRFu8w7bxhUF0X613DYwT4XzrZHpVyMe758hQ==
ts-loader@^9.5.2:
version "9.5.2"
resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.5.2.tgz#1f3d7f4bb709b487aaa260e8f19b301635d08020"
integrity sha512-Qo4piXvOTWcMGIgRiuFa6nHNm+54HbYaZCKqc9eeZCLRy3XqafQgwX2F7mofrbJG3g7EEb+lkiR+z2Lic2s3Zw==
dependencies:
chalk "^4.1.0"
enhanced-resolve "^5.0.0"

View File

@@ -88,7 +88,7 @@ dependencies = [
"python-dateutil",
"python-dotenv", # optional dependencies for Flask but required for Superset, see https://flask.palletsprojects.com/en/stable/installation/#optional-dependencies
"python-geohash",
"pyarrow>=16.1.0, <19", # before upgrading pyarrow, check that all db dependencies support this, see e.g. https://github.com/apache/superset/pull/34693
"pyarrow>=16.1.0, <17", # before upgrading pyarrow, check that all db dependencies support this, see e.g. https://github.com/apache/superset/pull/34693
"pyyaml>=6.0.0, <7.0.0",
"PyJWT>=2.4.0, <3.0",
"redis>=4.6.0, <5.0",

View File

@@ -19,9 +19,9 @@
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
"@reduxjs/toolkit": "^1.9.3",
"@rjsf/core": "^5.24.13",
"@rjsf/core": "^5.21.1",
"@rjsf/utils": "^5.24.3",
"@rjsf/validator-ajv8": "^5.24.13",
"@rjsf/validator-ajv8": "^5.24.12",
"@scarf/scarf": "^1.4.0",
"@superset-ui/chart-controls": "file:./packages/superset-ui-chart-controls",
"@superset-ui/core": "file:./packages/superset-ui-core",
@@ -57,7 +57,6 @@
"antd": "^5.24.6",
"chrono-node": "^2.7.8",
"classnames": "^2.2.5",
"content-disposition": "^0.5.4",
"d3-color": "^3.1.0",
"d3-scale": "^2.1.2",
"dayjs": "^1.11.13",
@@ -71,7 +70,7 @@
"fuse.js": "^7.0.0",
"geolib": "^2.0.24",
"geostyler": "^14.1.3",
"geostyler-data": "^1.1.0",
"geostyler-data": "^1.0.0",
"geostyler-openlayers-parser": "^4.3.0",
"geostyler-qgis-parser": "2.0.1",
"geostyler-style": "7.5.0",
@@ -85,7 +84,7 @@
"json-bigint": "^1.0.0",
"json-stringify-pretty-compact": "^2.0.0",
"lodash": "^4.17.21",
"luxon": "^3.7.1",
"luxon": "^3.5.0",
"mapbox-gl": "^3.13.0",
"markdown-to-jsx": "^7.7.4",
"match-sorter": "^6.3.4",
@@ -118,7 +117,7 @@
"react-split": "^2.0.9",
"react-table": "^7.8.0",
"react-transition-group": "^4.4.5",
"react-virtualized-auto-sizer": "^1.0.26",
"react-virtualized-auto-sizer": "^1.0.25",
"react-window": "^1.8.10",
"redux": "^4.2.1",
"redux-localstorage": "^0.4.1",
@@ -140,7 +139,7 @@
"@applitools/eyes-storybook": "^3.55.6",
"@babel/cli": "^7.27.2",
"@babel/compat-data": "^7.28.0",
"@babel/core": "^7.28.3",
"@babel/core": "^7.26.0",
"@babel/eslint-parser": "^7.25.9",
"@babel/node": "^7.22.6",
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
@@ -175,7 +174,6 @@
"@testing-library/react": "^12.1.5",
"@testing-library/react-hooks": "^8.0.1",
"@testing-library/user-event": "^12.8.3",
"@types/content-disposition": "^0.5.9",
"@types/dom-to-image": "^2.6.7",
"@types/jest": "^29.5.14",
"@types/js-levenshtein": "^1.1.3",
@@ -192,7 +190,7 @@
"@types/react-router-dom": "^5.3.3",
"@types/react-transition-group": "^4.4.12",
"@types/react-ultimate-pagination": "^1.2.4",
"@types/react-virtualized-auto-sizer": "^1.0.8",
"@types/react-virtualized-auto-sizer": "^1.0.4",
"@types/react-window": "^1.8.8",
"@types/redux-localstorage": "^1.0.8",
"@types/redux-mock-store": "^1.0.6",
@@ -210,7 +208,7 @@
"babel-plugin-typescript-to-proptypes": "^2.0.0",
"cheerio": "1.1.0",
"copy-webpack-plugin": "^13.0.0",
"cross-env": "^10.0.0",
"cross-env": "^7.0.3",
"css-loader": "^7.1.2",
"css-minimizer-webpack-plugin": "^7.0.2",
"eslint": "^8.56.0",
@@ -235,7 +233,7 @@
"eslint-plugin-testing-library": "^6.4.0",
"eslint-plugin-theme-colors": "file:eslint-rules/eslint-plugin-theme-colors",
"fetch-mock": "^11.1.5",
"fork-ts-checker-webpack-plugin": "^9.1.0",
"fork-ts-checker-webpack-plugin": "^9.0.2",
"history": "^5.3.0",
"html-webpack-plugin": "^5.6.3",
"imports-loader": "^5.0.0",
@@ -1101,22 +1099,22 @@
}
},
"node_modules/@babel/core": {
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz",
"integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==",
"version": "7.27.7",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.7.tgz",
"integrity": "sha512-BU2f9tlKQ5CAthiMIgpzAh4eDTLWo1mqi9jqE2OxMG0E/OM199VJt2q8BztTxpnSW0i1ymdwLXRJnYzvDM5r2w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@ampproject/remapping": "^2.2.0",
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.3",
"@babel/generator": "^7.27.5",
"@babel/helper-compilation-targets": "^7.27.2",
"@babel/helper-module-transforms": "^7.28.3",
"@babel/helpers": "^7.28.3",
"@babel/parser": "^7.28.3",
"@babel/helper-module-transforms": "^7.27.3",
"@babel/helpers": "^7.27.6",
"@babel/parser": "^7.27.7",
"@babel/template": "^7.27.2",
"@babel/traverse": "^7.28.3",
"@babel/types": "^7.28.2",
"@babel/traverse": "^7.27.7",
"@babel/types": "^7.27.7",
"convert-source-map": "^2.0.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.2",
@@ -1171,15 +1169,15 @@
}
},
"node_modules/@babel/generator": {
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz",
"integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==",
"version": "7.27.5",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.5.tgz",
"integrity": "sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==",
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.28.3",
"@babel/types": "^7.28.2",
"@jridgewell/gen-mapping": "^0.3.12",
"@jridgewell/trace-mapping": "^0.3.28",
"@babel/parser": "^7.27.5",
"@babel/types": "^7.27.3",
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.25",
"jsesc": "^3.0.2"
},
"engines": {
@@ -1303,15 +1301,6 @@
"@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
}
},
"node_modules/@babel/helper-globals": {
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
"integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-member-expression-to-functions": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz",
@@ -1340,15 +1329,15 @@
}
},
"node_modules/@babel/helper-module-transforms": {
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz",
"integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==",
"version": "7.27.3",
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz",
"integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-module-imports": "^7.27.1",
"@babel/helper-validator-identifier": "^7.27.1",
"@babel/traverse": "^7.28.3"
"@babel/traverse": "^7.27.3"
},
"engines": {
"node": ">=6.9.0"
@@ -1474,14 +1463,14 @@
}
},
"node_modules/@babel/helpers": {
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.3.tgz",
"integrity": "sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==",
"version": "7.27.6",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz",
"integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/template": "^7.27.2",
"@babel/types": "^7.28.2"
"@babel/types": "^7.27.6"
},
"engines": {
"node": ">=6.9.0"
@@ -1512,12 +1501,12 @@
}
},
"node_modules/@babel/parser": {
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz",
"integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==",
"version": "7.27.7",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.7.tgz",
"integrity": "sha512-qnzXzDXdr/po3bOTbTIQZ7+TxNKxpkN5IifVLXS+r7qwynkZfPyjZfE7hCXbo7IoO9TNcSyibgONsf2HauUd3Q==",
"license": "MIT",
"dependencies": {
"@babel/types": "^7.28.2"
"@babel/types": "^7.27.7"
},
"bin": {
"parser": "bin/babel-parser.js"
@@ -3156,27 +3145,27 @@
}
},
"node_modules/@babel/traverse": {
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz",
"integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==",
"version": "7.27.7",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.7.tgz",
"integrity": "sha512-X6ZlfR/O/s5EQ/SnUSLzr+6kGnkg8HXGMzpgsMsrJVcfDtH1vIp6ctCN4eZ1LS5c0+te5Cb6Y514fASjMRJ1nw==",
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.3",
"@babel/helper-globals": "^7.28.0",
"@babel/parser": "^7.28.3",
"@babel/generator": "^7.27.5",
"@babel/parser": "^7.27.7",
"@babel/template": "^7.27.2",
"@babel/types": "^7.28.2",
"debug": "^4.3.1"
"@babel/types": "^7.27.7",
"debug": "^4.3.1",
"globals": "^11.1.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/types": {
"version": "7.28.2",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz",
"integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==",
"version": "7.27.7",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.7.tgz",
"integrity": "sha512-8OLQgDScAOHXnAz2cV+RfzzNMipuLVBz2biuAJFMV9bfkNf393je3VM8CLkjQodW5+iWsSJdSgSWT6rsZoXHPw==",
"license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.27.1",
@@ -4591,13 +4580,6 @@
"global-box": "*"
}
},
"node_modules/@epic-web/invariant": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz",
"integrity": "sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==",
"dev": true,
"license": "MIT"
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz",
@@ -7040,13 +7022,17 @@
}
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
"version": "0.3.8",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
"integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.0",
"@jridgewell/set-array": "^1.2.1",
"@jridgewell/sourcemap-codec": "^1.4.10",
"@jridgewell/trace-mapping": "^0.3.24"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/resolve-uri": {
@@ -7058,6 +7044,15 @@
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/set-array": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
"integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
"license": "MIT",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/source-map": {
"version": "0.3.6",
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
@@ -7076,9 +7071,9 @@
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.30",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz",
"integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==",
"version": "0.3.25",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
"integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
"license": "MIT",
"dependencies": {
"@jridgewell/resolve-uri": "^3.1.0",
@@ -10848,14 +10843,15 @@
"license": "MIT"
},
"node_modules/@rjsf/core": {
"version": "5.24.13",
"resolved": "https://registry.npmjs.org/@rjsf/core/-/core-5.24.13.tgz",
"integrity": "sha512-ONTr14s7LFIjx2VRFLuOpagL76sM/HPy6/OhdBfq6UukINmTIs6+aFN0GgcR0aXQHFDXQ7f/fel0o/SO05Htdg==",
"version": "5.24.1",
"resolved": "https://registry.npmjs.org/@rjsf/core/-/core-5.24.1.tgz",
"integrity": "sha512-x3oZ9MfoBA4SPKhvNkqagqnjUbSKuT3gGW3NgH+cowAma33fYs9/hfhPUOvXySdPqh0layuLlnI1W5MHeBmVzg==",
"license": "Apache-2.0",
"dependencies": {
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"markdown-to-jsx": "^7.4.1",
"nanoid": "^3.3.7",
"prop-types": "^15.8.1"
},
"engines": {
@@ -10866,6 +10862,24 @@
"react": "^16.14.0 || >=17"
}
},
"node_modules/@rjsf/core/node_modules/nanoid": {
"version": "3.3.8",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
"integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/@rjsf/utils": {
"version": "5.24.3",
"resolved": "https://registry.npmjs.org/@rjsf/utils/-/utils-5.24.3.tgz",
@@ -10886,9 +10900,9 @@
}
},
"node_modules/@rjsf/validator-ajv8": {
"version": "5.24.13",
"resolved": "https://registry.npmjs.org/@rjsf/validator-ajv8/-/validator-ajv8-5.24.13.tgz",
"integrity": "sha512-oWHP7YK581M8I5cF1t+UXFavnv+bhcqjtL1a7MG/Kaffi0EwhgcYjODrD8SsnrhncsEYMqSECr4ZOEoirnEUWw==",
"version": "5.24.12",
"resolved": "https://registry.npmjs.org/@rjsf/validator-ajv8/-/validator-ajv8-5.24.12.tgz",
"integrity": "sha512-IMXdCjvDNdvb+mDgZC3AlAtr0pjYKq5s0GcLECjG5PuiX7Ib4JaDQHZY5ZJdKblMfgzhsn8AAOi573jXAt7BHQ==",
"license": "Apache-2.0",
"dependencies": {
"ajv": "^8.12.0",
@@ -15252,13 +15266,6 @@
"@types/node": "*"
}
},
"node_modules/@types/content-disposition": {
"version": "0.5.9",
"resolved": "https://registry.npmjs.org/@types/content-disposition/-/content-disposition-0.5.9.tgz",
"integrity": "sha512-8uYXI3Gw35MhiVYhG3s295oihrxRyytcRHjSjqnqZVDDy/xcGBRny7+Xj1Wgfhv5QzRtN2hB2dVRBUX9XW3UcQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/conventional-commits-parser": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/@types/conventional-commits-parser/-/conventional-commits-parser-5.0.1.tgz",
@@ -16089,14 +16096,13 @@
}
},
"node_modules/@types/react-virtualized-auto-sizer": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.8.tgz",
"integrity": "sha512-keJpNyhiwfl2+N12G1ocCVA5ZDBArbPLe/S90X3kt7fam9naeHdaYYWbpe2sHczp70JWJ+2QLhBE8kLvLuVNjA==",
"deprecated": "This is a stub types definition. react-virtualized-auto-sizer provides its own type definitions, so you do not need this installed.",
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@types/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.4.tgz",
"integrity": "sha512-nhYwlFiYa8M3S+O2T9QO/e1FQUYMr/wJENUdf/O0dhRi1RS/93rjrYQFYdbUqtdFySuhrtnEDX29P6eKOttY+A==",
"dev": true,
"license": "MIT",
"dependencies": {
"react-virtualized-auto-sizer": "*"
"@types/react": "*"
}
},
"node_modules/@types/react-window": {
@@ -21937,6 +21943,7 @@
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"safe-buffer": "5.2.1"
@@ -22350,21 +22357,22 @@
"license": "MIT"
},
"node_modules/cross-env": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.0.0.tgz",
"integrity": "sha512-aU8qlEK/nHYtVuN4p7UQgAwVljzMg8hB4YK5ThRqD2l/ziSnryncPNn7bMLt5cFYsKVKBh8HqLqyCoTupEUu7Q==",
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
"integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@epic-web/invariant": "^1.0.0",
"cross-spawn": "^7.0.6"
"cross-spawn": "^7.0.1"
},
"bin": {
"cross-env": "dist/bin/cross-env.js",
"cross-env-shell": "dist/bin/cross-env-shell.js"
"cross-env": "src/bin/cross-env.js",
"cross-env-shell": "src/bin/cross-env-shell.js"
},
"engines": {
"node": ">=20"
"node": ">=10.14",
"npm": ">=6",
"yarn": ">=1"
}
},
"node_modules/cross-spawn": {
@@ -27480,15 +27488,15 @@
}
},
"node_modules/fork-ts-checker-webpack-plugin": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-9.1.0.tgz",
"integrity": "sha512-mpafl89VFPJmhnJ1ssH+8wmM2b50n+Rew5x42NeI2U78aRWgtkEtGmctp7iT16UjquJTjorEmIfESj3DxdW84Q==",
"version": "9.0.2",
"resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-9.0.2.tgz",
"integrity": "sha512-Uochze2R8peoN1XqlSi/rGUkDQpRogtLFocP9+PGu68zk1BDAKXfdeCdyVZpgTk8V8WFVQXdEz426VKjXLO1Gg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.16.7",
"chalk": "^4.1.2",
"chokidar": "^4.0.1",
"chokidar": "^3.5.3",
"cosmiconfig": "^8.2.0",
"deepmerge": "^4.2.2",
"fs-extra": "^10.0.0",
@@ -27500,7 +27508,8 @@
"tapable": "^2.2.1"
},
"engines": {
"node": ">=14.21.3"
"node": ">=12.13.0",
"yarn": ">=1.0.0"
},
"peerDependencies": {
"typescript": ">3.6.0",
@@ -27551,22 +27560,6 @@
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/fork-ts-checker-webpack-plugin/node_modules/chokidar": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
"dev": true,
"license": "MIT",
"dependencies": {
"readdirp": "^4.0.1"
},
"engines": {
"node": ">= 14.16.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/fork-ts-checker-webpack-plugin/node_modules/fs-extra": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
@@ -27589,20 +27582,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/fork-ts-checker-webpack-plugin/node_modules/readdirp": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
"integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 14.18.0"
},
"funding": {
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/fork-ts-checker-webpack-plugin/node_modules/schema-utils": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
@@ -28067,18 +28046,27 @@
}
},
"node_modules/geostyler-data": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/geostyler-data/-/geostyler-data-1.1.0.tgz",
"integrity": "sha512-0tUEF0RbiGM9eVqoLbMc20Bl5A1x3PHbn2Ca0yPJx65S0nh3rHfd82OmgFwHzzfsukDmqIn0VoOGJFjmQAmAcw==",
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/geostyler-data/-/geostyler-data-1.0.0.tgz",
"integrity": "sha512-ctmk6OsunL427Uaa1HME/blTyBbl0Ihu+vPV1Irqz3ip80qvNLwDEr46xI5HwMeyrsWH8o76kfA0sF6oecW1BA==",
"license": "BSD-2-Clause",
"dependencies": {
"@types/geojson": "^7946.0.7",
"@types/json-schema": "^7.0.3"
},
"funding": {
"url": "https://opencollective.com/geostyler"
"@types/geojson": "7946.0.7",
"@types/json-schema": "7.0.3"
}
},
"node_modules/geostyler-data/node_modules/@types/geojson": {
"version": "7946.0.7",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.7.tgz",
"integrity": "sha512-wE2v81i4C4Ol09RtsWFAqg3BUitWbHSpSlIo+bNdsCJijO9sjme+zm+73ZMCa/qMC8UEERxzGbvmr1cffo2SiQ==",
"license": "MIT"
},
"node_modules/geostyler-data/node_modules/@types/json-schema": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.3.tgz",
"integrity": "sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A==",
"license": "MIT"
},
"node_modules/geostyler-geojson-parser": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/geostyler-geojson-parser/-/geostyler-geojson-parser-1.0.1.tgz",
@@ -29282,7 +29270,6 @@
"version": "11.12.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=4"
@@ -37680,9 +37667,9 @@
}
},
"node_modules/luxon": {
"version": "3.7.1",
"resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.1.tgz",
"integrity": "sha512-RkRWjA926cTvz5rAb1BqyWkKbbjzCGchDUIKMCUvNi17j6f6j8uHGDV82Aqcqtzd+icoYpELmG3ksgGiFNNcNg==",
"version": "3.6.1",
"resolved": "https://registry.npmjs.org/luxon/-/luxon-3.6.1.tgz",
"integrity": "sha512-tJLxrKJhO2ukZ5z0gyjY1zPh3Rh88Ej9P7jNrZiHMUXHae1yvI2imgOZtL1TO8TW6biMMKfTtAOoEJANgtWBMQ==",
"license": "MIT",
"engines": {
"node": ">=12"
@@ -46373,9 +46360,9 @@
}
},
"node_modules/prismjs": {
"version": "1.30.0",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz",
"integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==",
"version": "1.29.0",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz",
"integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==",
"license": "MIT",
"engines": {
"node": ">=6"
@@ -48560,16 +48547,16 @@
}
},
"node_modules/react-syntax-highlighter": {
"version": "15.6.6",
"resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.6.6.tgz",
"integrity": "sha512-DgXrc+AZF47+HvAPEmn7Ua/1p10jNoVZVI/LoPiYdtY+OM+/nG5yefLHKJwdKqY1adMuHFbeyBaG9j64ML7vTw==",
"version": "15.6.1",
"resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.6.1.tgz",
"integrity": "sha512-OqJ2/vL7lEeV5zTJyG7kmARppUjiB9h9udl4qHQjjgEos66z00Ia0OckwYfRxCSFrW8RJIBnsBwQsHZbVPspqg==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.3.1",
"highlight.js": "^10.4.1",
"highlightjs-vue": "^1.0.0",
"lowlight": "^1.17.0",
"prismjs": "^1.30.0",
"prismjs": "^1.27.0",
"refractor": "^3.6.0"
},
"peerDependencies": {
@@ -48657,9 +48644,9 @@
}
},
"node_modules/react-virtualized-auto-sizer": {
"version": "1.0.26",
"resolved": "https://registry.npmjs.org/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.26.tgz",
"integrity": "sha512-CblNyiNVw2o+hsa5/49NH2ogGxZ+t+3aweRvNSq7TVjDIlwk7ir4lencEg5HxHeSzwNarSkNkiu0qJSOXtxm5A==",
"version": "1.0.25",
"resolved": "https://registry.npmjs.org/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.25.tgz",
"integrity": "sha512-YHsksEGDfsHbHuaBVDYwJmcktblcHGafz4ZVuYPQYuSHMUGjpwmUCrAOcvMSGMwwk1eFWj1M/1GwYpNPuyhaBg==",
"license": "MIT",
"peerDependencies": {
"react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 || ^19.0.0",
@@ -58637,7 +58624,7 @@
"yosay": "^3.0.0"
},
"devDependencies": {
"cross-env": "^10.0.0",
"cross-env": "^7.0.3",
"fs-extra": "^11.3.0",
"jest": "^30.0.5",
"yeoman-test": "^10.1.1"
@@ -60646,7 +60633,7 @@
"license": "ISC",
"devDependencies": {
"@babel/cli": "^7.26.4",
"@babel/core": "^7.28.3",
"@babel/core": "^7.26.9",
"@babel/preset-env": "^7.26.9",
"@babel/preset-react": "^7.26.3",
"@babel/preset-typescript": "^7.26.0",
@@ -63398,7 +63385,7 @@
"react-js-cron": "^5.2.0",
"react-markdown": "^8.0.7",
"react-resize-detector": "^7.1.2",
"react-syntax-highlighter": "^15.6.6",
"react-syntax-highlighter": "^15.4.5",
"react-ultimate-pagination": "^1.3.2",
"regenerator-runtime": "^0.14.1",
"rehype-raw": "^7.0.0",
@@ -64414,7 +64401,7 @@
"react-resizable": "^3.0.5"
},
"devDependencies": {
"@babel/core": "^7.28.3",
"@babel/core": "^7.26.0",
"@babel/preset-env": "^7.27.2",
"@babel/preset-react": "^7.27.1",
"@babel/preset-typescript": "^7.23.3",
@@ -64804,6 +64791,13 @@
"ajv": "^6.9.1"
}
},
"packages/superset-ui-demo/node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true,
"license": "Python-2.0"
},
"packages/superset-ui-demo/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -64885,6 +64879,105 @@
}
}
},
"packages/superset-ui-demo/node_modules/fork-ts-checker-webpack-plugin": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-9.1.0.tgz",
"integrity": "sha512-mpafl89VFPJmhnJ1ssH+8wmM2b50n+Rew5x42NeI2U78aRWgtkEtGmctp7iT16UjquJTjorEmIfESj3DxdW84Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.16.7",
"chalk": "^4.1.2",
"chokidar": "^4.0.1",
"cosmiconfig": "^8.2.0",
"deepmerge": "^4.2.2",
"fs-extra": "^10.0.0",
"memfs": "^3.4.1",
"minimatch": "^3.0.4",
"node-abort-controller": "^3.0.1",
"schema-utils": "^3.1.1",
"semver": "^7.3.5",
"tapable": "^2.2.1"
},
"engines": {
"node": ">=14.21.3"
},
"peerDependencies": {
"typescript": ">3.6.0",
"webpack": "^5.11.0"
}
},
"packages/superset-ui-demo/node_modules/fork-ts-checker-webpack-plugin/node_modules/chokidar": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
"dev": true,
"license": "MIT",
"dependencies": {
"readdirp": "^4.0.1"
},
"engines": {
"node": ">= 14.16.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"packages/superset-ui-demo/node_modules/fork-ts-checker-webpack-plugin/node_modules/cosmiconfig": {
"version": "8.3.6",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz",
"integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==",
"dev": true,
"license": "MIT",
"dependencies": {
"import-fresh": "^3.3.0",
"js-yaml": "^4.1.0",
"parse-json": "^5.2.0",
"path-type": "^4.0.0"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/d-fischer"
},
"peerDependencies": {
"typescript": ">=4.9.5"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"packages/superset-ui-demo/node_modules/fork-ts-checker-webpack-plugin/node_modules/fs-extra": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=12"
}
},
"packages/superset-ui-demo/node_modules/js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"dev": true,
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
},
"bin": {
"js-yaml": "bin/js-yaml.js"
}
},
"packages/superset-ui-demo/node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
@@ -64892,6 +64985,20 @@
"dev": true,
"license": "MIT"
},
"packages/superset-ui-demo/node_modules/readdirp": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
"integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 14.18.0"
},
"funding": {
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
},
"packages/superset-ui-demo/node_modules/schema-utils": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",

View File

@@ -87,9 +87,9 @@
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
"@reduxjs/toolkit": "^1.9.3",
"@rjsf/core": "^5.24.13",
"@rjsf/core": "^5.21.1",
"@rjsf/utils": "^5.24.3",
"@rjsf/validator-ajv8": "^5.24.13",
"@rjsf/validator-ajv8": "^5.24.12",
"@scarf/scarf": "^1.4.0",
"@superset-ui/chart-controls": "file:./packages/superset-ui-chart-controls",
"@superset-ui/core": "file:./packages/superset-ui-core",
@@ -125,7 +125,6 @@
"antd": "^5.24.6",
"chrono-node": "^2.7.8",
"classnames": "^2.2.5",
"content-disposition": "^0.5.4",
"d3-color": "^3.1.0",
"d3-scale": "^2.1.2",
"dayjs": "^1.11.13",
@@ -139,7 +138,7 @@
"fuse.js": "^7.0.0",
"geolib": "^2.0.24",
"geostyler": "^14.1.3",
"geostyler-data": "^1.1.0",
"geostyler-data": "^1.0.0",
"geostyler-openlayers-parser": "^4.3.0",
"geostyler-qgis-parser": "2.0.1",
"geostyler-style": "7.5.0",
@@ -153,7 +152,7 @@
"json-bigint": "^1.0.0",
"json-stringify-pretty-compact": "^2.0.0",
"lodash": "^4.17.21",
"luxon": "^3.7.1",
"luxon": "^3.5.0",
"mapbox-gl": "^3.13.0",
"markdown-to-jsx": "^7.7.4",
"match-sorter": "^6.3.4",
@@ -186,7 +185,7 @@
"react-split": "^2.0.9",
"react-table": "^7.8.0",
"react-transition-group": "^4.4.5",
"react-virtualized-auto-sizer": "^1.0.26",
"react-virtualized-auto-sizer": "^1.0.25",
"react-window": "^1.8.10",
"redux": "^4.2.1",
"redux-localstorage": "^0.4.1",
@@ -208,7 +207,7 @@
"@applitools/eyes-storybook": "^3.55.6",
"@babel/cli": "^7.27.2",
"@babel/compat-data": "^7.28.0",
"@babel/core": "^7.28.3",
"@babel/core": "^7.26.0",
"@babel/eslint-parser": "^7.25.9",
"@babel/node": "^7.22.6",
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
@@ -243,7 +242,6 @@
"@testing-library/react": "^12.1.5",
"@testing-library/react-hooks": "^8.0.1",
"@testing-library/user-event": "^12.8.3",
"@types/content-disposition": "^0.5.9",
"@types/dom-to-image": "^2.6.7",
"@types/jest": "^29.5.14",
"@types/js-levenshtein": "^1.1.3",
@@ -260,7 +258,7 @@
"@types/react-router-dom": "^5.3.3",
"@types/react-transition-group": "^4.4.12",
"@types/react-ultimate-pagination": "^1.2.4",
"@types/react-virtualized-auto-sizer": "^1.0.8",
"@types/react-virtualized-auto-sizer": "^1.0.4",
"@types/react-window": "^1.8.8",
"@types/redux-localstorage": "^1.0.8",
"@types/redux-mock-store": "^1.0.6",
@@ -278,7 +276,7 @@
"babel-plugin-typescript-to-proptypes": "^2.0.0",
"cheerio": "1.1.0",
"copy-webpack-plugin": "^13.0.0",
"cross-env": "^10.0.0",
"cross-env": "^7.0.3",
"css-loader": "^7.1.2",
"css-minimizer-webpack-plugin": "^7.0.2",
"eslint": "^8.56.0",
@@ -303,7 +301,7 @@
"eslint-plugin-testing-library": "^6.4.0",
"eslint-plugin-theme-colors": "file:eslint-rules/eslint-plugin-theme-colors",
"fetch-mock": "^11.1.5",
"fork-ts-checker-webpack-plugin": "^9.1.0",
"fork-ts-checker-webpack-plugin": "^9.0.2",
"history": "^5.3.0",
"html-webpack-plugin": "^5.6.3",
"imports-loader": "^5.0.0",

View File

@@ -34,7 +34,7 @@
"yosay": "^3.0.0"
},
"devDependencies": {
"cross-env": "^10.0.0",
"cross-env": "^7.0.3",
"fs-extra": "^11.3.0",
"jest": "^30.0.5",
"yeoman-test": "^10.1.1"

View File

@@ -1,32 +1,32 @@
{
"name": "@apache-superset/core",
"version": "0.0.1-rc3",
"version": "0.0.1-rc2",
"description": "This package contains UI elements, APIs, and utility functions used by Superset.",
"sideEffects": false,
"main": "lib/index.js",
"types": "lib/index.d.ts",
"module": "esm/index.js",
"files": [
"esm",
"lib"
],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/cli": "^7.26.4",
"@babel/core": "^7.28.3",
"@babel/core": "^7.26.9",
"@babel/preset-env": "^7.26.9",
"@babel/preset-react": "^7.26.3",
"@babel/preset-typescript": "^7.26.0",
"@types/react": "^17.0.83",
"install": "^0.13.0",
"npm": "^11.1.0",
"typescript": "^5.0.0"
"npm": "^11.1.0"
},
"peerDependencies": {
"antd": "^5.24.6",
"react": "^17.0.2"
},
"scripts": {
"build": "babel src --out-dir lib --extensions \".ts,.tsx\" && tsc --emitDeclarationOnly",
"build": "babel src --out-dir lib --extensions \".ts,.tsx\"",
"type": "tsc --noEmit"
},
"publishConfig": {

View File

@@ -10,9 +10,7 @@
"baseUrl": ".",
"module": "esnext",
"moduleResolution": "node",
"skipLibCheck": true,
"target": "es2020",
"esModuleInterop": true
"skipLibCheck": true
},
"include": ["src/**/*.ts*"],
"exclude": ["lib"]

View File

@@ -43,17 +43,15 @@ export const renameOperator: PostProcessingFactory<PostProcessingRename> = (
// remove or rename top level of column name(metric name) in the MultiIndex when
// 1) at least 1 metric
// 2) xAxis exist
// 3a) isTimeComparisonValue
// 3b-1) dimension exist or multiple time shift metrics exist
// 3b-2) truncate_metric in form_data and truncate_metric is true
// 2) dimension exist or multiple time shift metrics exist
// 3) xAxis exist
// 4) truncate_metric in form_data and truncate_metric is true
if (
metrics.length > 0 &&
(columns.length > 0 || timeOffsets.length > 1) &&
xAxisLabel &&
(isTimeComparisonValue ||
((columns.length > 0 || timeOffsets.length > 1) &&
truncate_metric !== undefined &&
!!truncate_metric))
truncate_metric !== undefined &&
!!truncate_metric
) {
const renamePairs: [string, string | null][] = [];
if (

View File

@@ -160,44 +160,6 @@ test('should add renameOperator if exists derived metrics', () => {
});
});
test('should add renameOperator if isTimeComparisonValue without columns', () => {
[
ComparisonType.Difference,
ComparisonType.Ratio,
ComparisonType.Percentage,
].forEach(type => {
expect(
renameOperator(
{
...formData,
...{
comparison_type: type,
time_compare: ['1 year ago'],
},
},
{
...queryObject,
...{
columns: [],
metrics: ['sum(val)', 'avg(val2)'],
},
},
),
).toEqual({
operation: 'rename',
options: {
columns: {
[`${type}__avg(val2)__avg(val2)__1 year ago`]:
'avg(val2), 1 year ago',
[`${type}__sum(val)__sum(val)__1 year ago`]: 'sum(val), 1 year ago',
},
inplace: true,
level: 0,
},
});
});
});
test('should add renameOperator if x_axis does not exist', () => {
expect(
renameOperator(

View File

@@ -54,7 +54,7 @@
"react-js-cron": "^5.2.0",
"react-draggable": "^4.5.0",
"react-resize-detector": "^7.1.2",
"react-syntax-highlighter": "^15.6.6",
"react-syntax-highlighter": "^15.4.5",
"react-ultimate-pagination": "^1.3.2",
"react-error-boundary": "^6.0.0",
"react-markdown": "^8.0.7",

View File

@@ -393,7 +393,10 @@ export const FullSQLEditor = AsyncAceEditor(
},
);
export const MarkdownEditor = AsyncAceEditor(['mode/markdown', 'theme/github']);
export const MarkdownEditor = AsyncAceEditor([
'mode/markdown',
'theme/textmate',
]);
export const TextAreaEditor = AsyncAceEditor([
'mode/markdown',

View File

@@ -48,9 +48,15 @@ export const CollapseLabelInModal: React.FC<CollapseLabelInModalProps> = ({
{title}{' '}
{validateCheckStatus !== undefined &&
(validateCheckStatus ? (
<Icons.CheckCircleOutlined iconColor={theme.colorSuccess} />
<Icons.CheckCircleOutlined
iconColor={theme.colorSuccess}
aria-label="check-circle"
/>
) : (
<Icons.ExclamationCircleOutlined iconColor={theme.colorError} />
<Icons.ExclamationCircleOutlined
iconColor={theme.colorError}
aria-label="error-circle"
/>
))}
</Typography.Title>
<Typography.Paragraph

View File

@@ -22,7 +22,7 @@ import { AntdIconType, BaseIconProps, CustomIconType, IconType } from './types';
const genAriaLabel = (fileName: string) => {
const name = fileName.replace(/_/g, '-'); // Replace underscores with dashes
const words = name.split(/(?<=[a-z])(?=[A-Z])/); // Split at lowercase-to-uppercase transitions
const words = name.split(/(?=[A-Z])/); // Split at uppercase letters
if (words.length === 2) {
return words[0].toLowerCase();

View File

@@ -1,306 +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 { fireEvent, render } from '@superset-ui/core/spec';
import Tabs, { EditableTabs, LineEditableTabs } from './Tabs';
describe('Tabs', () => {
const defaultItems = [
{
key: '1',
label: 'Tab 1',
children: <div data-testid="tab1-content">Tab 1 content</div>,
},
{
key: '2',
label: 'Tab 2',
children: <div data-testid="tab2-content">Tab 2 content</div>,
},
{
key: '3',
label: 'Tab 3',
children: <div data-testid="tab3-content">Tab 3 content</div>,
},
];
describe('Basic Tabs', () => {
it('should render tabs with default props', () => {
const { getByText, container } = render(<Tabs items={defaultItems} />);
expect(getByText('Tab 1')).toBeInTheDocument();
expect(getByText('Tab 2')).toBeInTheDocument();
expect(getByText('Tab 3')).toBeInTheDocument();
const activeTabContent = container.querySelector(
'.ant-tabs-tabpane-active',
);
expect(activeTabContent).toBeDefined();
expect(
activeTabContent?.querySelector('[data-testid="tab1-content"]'),
).toBeDefined();
});
it('should render tabs component structure', () => {
const { container } = render(<Tabs items={defaultItems} />);
const tabsElement = container.querySelector('.ant-tabs');
const tabsNav = container.querySelector('.ant-tabs-nav');
const tabsContent = container.querySelector('.ant-tabs-content-holder');
expect(tabsElement).toBeDefined();
expect(tabsNav).toBeDefined();
expect(tabsContent).toBeDefined();
});
it('should apply default tabBarStyle with padding', () => {
const { container } = render(<Tabs items={defaultItems} />);
const tabsNav = container.querySelector('.ant-tabs-nav') as HTMLElement;
// Check that tabBarStyle is applied (default padding is added)
expect(tabsNav?.style?.paddingLeft).toBeDefined();
});
it('should merge custom tabBarStyle with defaults', () => {
const customStyle = { paddingRight: '20px', backgroundColor: 'red' };
const { container } = render(
<Tabs items={defaultItems} tabBarStyle={customStyle} />,
);
const tabsNav = container.querySelector('.ant-tabs-nav') as HTMLElement;
expect(tabsNav?.style?.paddingLeft).toBeDefined();
expect(tabsNav?.style?.paddingRight).toBe('20px');
expect(tabsNav?.style?.backgroundColor).toBe('red');
});
it('should handle allowOverflow prop', () => {
const { container: allowContainer } = render(
<Tabs items={defaultItems} allowOverflow />,
);
const { container: disallowContainer } = render(
<Tabs items={defaultItems} allowOverflow={false} />,
);
expect(allowContainer.querySelector('.ant-tabs')).toBeDefined();
expect(disallowContainer.querySelector('.ant-tabs')).toBeDefined();
});
it('should disable animation by default', () => {
const { container } = render(<Tabs items={defaultItems} />);
const tabsElement = container.querySelector('.ant-tabs');
expect(tabsElement?.className).not.toContain('ant-tabs-animated');
});
it('should handle tab change events', () => {
const onChangeMock = jest.fn();
const { getByText } = render(
<Tabs items={defaultItems} onChange={onChangeMock} />,
);
fireEvent.click(getByText('Tab 2'));
expect(onChangeMock).toHaveBeenCalledWith('2');
});
it('should pass through additional props to Antd Tabs', () => {
const onTabClickMock = jest.fn();
const { getByText } = render(
<Tabs
items={defaultItems}
onTabClick={onTabClickMock}
size="large"
centered
/>,
);
fireEvent.click(getByText('Tab 2'));
expect(onTabClickMock).toHaveBeenCalled();
});
});
describe('EditableTabs', () => {
it('should render with editable features', () => {
const { container } = render(<EditableTabs items={defaultItems} />);
const tabsElement = container.querySelector('.ant-tabs');
expect(tabsElement?.className).toContain('ant-tabs-card');
expect(tabsElement?.className).toContain('ant-tabs-editable-card');
});
it('should handle onEdit callback for add/remove actions', () => {
const onEditMock = jest.fn();
const itemsWithRemove = defaultItems.map(item => ({
...item,
closable: true,
}));
const { container } = render(
<EditableTabs items={itemsWithRemove} onEdit={onEditMock} />,
);
const removeButton = container.querySelector('.ant-tabs-tab-remove');
expect(removeButton).toBeDefined();
fireEvent.click(removeButton!);
expect(onEditMock).toHaveBeenCalledWith(expect.any(String), 'remove');
});
it('should have default props set correctly', () => {
expect(EditableTabs.defaultProps?.type).toBe('editable-card');
expect(EditableTabs.defaultProps?.animated).toEqual({
inkBar: true,
tabPane: false,
});
});
});
describe('LineEditableTabs', () => {
it('should render as line-style editable tabs', () => {
const { container } = render(<LineEditableTabs items={defaultItems} />);
const tabsElement = container.querySelector('.ant-tabs');
expect(tabsElement?.className).toContain('ant-tabs-card');
expect(tabsElement?.className).toContain('ant-tabs-editable-card');
});
it('should render with line-specific styling', () => {
const { container } = render(<LineEditableTabs items={defaultItems} />);
const inkBar = container.querySelector('.ant-tabs-ink-bar');
expect(inkBar).toBeDefined();
});
});
describe('TabPane Legacy Support', () => {
it('should support TabPane component access', () => {
expect(Tabs.TabPane).toBeDefined();
expect(EditableTabs.TabPane).toBeDefined();
expect(LineEditableTabs.TabPane).toBeDefined();
});
it('should render using legacy TabPane syntax', () => {
const { getByText, container } = render(
<Tabs>
<Tabs.TabPane tab="Legacy Tab 1" key="1">
<div data-testid="legacy-content-1">Legacy content 1</div>
</Tabs.TabPane>
<Tabs.TabPane tab="Legacy Tab 2" key="2">
<div data-testid="legacy-content-2">Legacy content 2</div>
</Tabs.TabPane>
</Tabs>,
);
expect(getByText('Legacy Tab 1')).toBeInTheDocument();
expect(getByText('Legacy Tab 2')).toBeInTheDocument();
const activeTabContent = container.querySelector(
'.ant-tabs-tabpane-active [data-testid="legacy-content-1"]',
);
expect(activeTabContent).toBeDefined();
expect(activeTabContent?.textContent).toBe('Legacy content 1');
});
});
describe('Edge Cases', () => {
it('should handle empty items array', () => {
const { container } = render(<Tabs items={[]} />);
const tabsElement = container.querySelector('.ant-tabs');
expect(tabsElement).toBeDefined();
});
it('should handle undefined items', () => {
const { container } = render(<Tabs />);
const tabsElement = container.querySelector('.ant-tabs');
expect(tabsElement).toBeDefined();
});
it('should handle tabs with no content', () => {
const itemsWithoutContent = [
{ key: '1', label: 'Tab 1' },
{ key: '2', label: 'Tab 2' },
];
const { getByText } = render(<Tabs items={itemsWithoutContent} />);
expect(getByText('Tab 1')).toBeInTheDocument();
expect(getByText('Tab 2')).toBeInTheDocument();
});
it('should handle allowOverflow default value', () => {
const { container } = render(<Tabs items={defaultItems} />);
expect(container.querySelector('.ant-tabs')).toBeDefined();
});
});
describe('Accessibility', () => {
it('should render with proper ARIA roles', () => {
const { container } = render(<Tabs items={defaultItems} />);
const tablist = container.querySelector('[role="tablist"]');
const tabs = container.querySelectorAll('[role="tab"]');
expect(tablist).toBeDefined();
expect(tabs.length).toBe(3);
});
it('should support keyboard navigation', () => {
const { container, getByText } = render(<Tabs items={defaultItems} />);
const firstTab = container.querySelector('[role="tab"]');
const secondTab = getByText('Tab 2');
if (firstTab) {
fireEvent.keyDown(firstTab, { key: 'ArrowRight', code: 'ArrowRight' });
}
fireEvent.click(secondTab);
expect(secondTab).toBeInTheDocument();
});
});
describe('Styling Integration', () => {
it('should accept and apply custom CSS classes', () => {
const { container } = render(
<Tabs items={defaultItems} className="custom-tabs-class" />,
);
const tabsElement = container.querySelector('.ant-tabs');
expect(tabsElement?.className).toContain('custom-tabs-class');
});
it('should accept and apply custom styles', () => {
const customStyle = { minHeight: '200px' };
const { container } = render(
<Tabs items={defaultItems} style={customStyle} />,
);
const tabsElement = container.querySelector('.ant-tabs') as HTMLElement;
expect(tabsElement?.style?.minHeight).toBe('200px');
});
});
});

View File

@@ -29,18 +29,14 @@ export interface TabsProps extends AntdTabsProps {
const StyledTabs = ({
animated = false,
allowOverflow = true,
tabBarStyle,
...props
}: TabsProps) => {
const theme = useTheme();
const defaultTabBarStyle = { paddingLeft: theme.sizeUnit * 4 };
const mergedStyle = { ...defaultTabBarStyle, ...tabBarStyle };
return (
<AntdTabs
animated={animated}
{...props}
tabBarStyle={mergedStyle}
tabBarStyle={{ paddingLeft: theme.sizeUnit * 4 }}
css={theme => css`
overflow: ${allowOverflow ? 'visible' : 'hidden'};

View File

@@ -52,7 +52,7 @@
"react-resizable": "^3.0.5"
},
"devDependencies": {
"@babel/core": "^7.28.3",
"@babel/core": "^7.26.0",
"@babel/preset-env": "^7.27.2",
"@babel/preset-react": "^7.27.1",
"@babel/preset-typescript": "^7.23.3",

View File

@@ -58,13 +58,11 @@ export default styled(CountryMap)`
}
.superset-legacy-chart-country-map text.result-text {
fill: ${theme.colorText};
font-weight: ${theme.fontWeightLight};
font-size: ${theme.fontSizeXL}px;
}
.superset-legacy-chart-country-map text.big-text {
fill: ${theme.colorText};
font-weight: ${theme.fontWeightStrong};
font-size: ${theme.fontSizeLG}px;
}

View File

@@ -128,7 +128,6 @@ export default function transformProps(chartProps: EchartsBubbleChartProps) {
legendOrientation,
legendMargin,
legendType,
legendSort,
sliceId,
}: EchartsBubbleFormData = { ...DEFAULT_FORM_DATA, ...formData };
const colorFn = CategoricalColorNamespace.getScale(colorScheme as string);
@@ -231,10 +230,7 @@ export default function transformProps(chartProps: EchartsBubbleChartProps) {
},
legend: {
...getLegendProps(legendType, legendOrientation, showLegend, theme),
data: Array.from(legends).sort((a: string, b: string) => {
if (!legendSort) return 0;
return legendSort === 'asc' ? a.localeCompare(b) : b.localeCompare(a);
}),
data: Array.from(legends),
},
tooltip: {
show: !inContextMenu,

View File

@@ -112,7 +112,6 @@ export default function transformProps(
legendMargin,
legendOrientation,
legendType,
legendSort,
metric = '',
numberFormat,
currencyFormat,
@@ -291,10 +290,7 @@ export default function transformProps(
},
legend: {
...getLegendProps(legendType, legendOrientation, showLegend, theme),
data: keys.sort((a: string, b: string) => {
if (!legendSort) return 0;
return legendSort === 'asc' ? a.localeCompare(b) : b.localeCompare(a);
}),
data: keys,
},
series,
};

View File

@@ -131,7 +131,6 @@ export default function transformProps(chartProps: EchartsGanttChartProps) {
legendMargin,
legendOrientation,
legendType,
legendSort,
showLegend,
yAxisTitle,
yAxisTitleMargin,
@@ -331,18 +330,6 @@ export default function transformProps(chartProps: EchartsGanttChartProps) {
},
);
const legendData = series
.map(entry => {
const { name } = entry;
if (name === null || name === undefined) return '';
return String(name);
})
.filter(name => name !== '')
.sort((a, b) => {
if (!legendSort) return 0;
return legendSort === 'asc' ? a.localeCompare(b) : b.localeCompare(a);
});
const tooltipFormatterMap = {
[GenericDataType.Numeric]: tooltipValuesFormatter,
[GenericDataType.String]: undefined,
@@ -378,7 +365,6 @@ export default function transformProps(chartProps: EchartsGanttChartProps) {
zoomable,
legendState,
),
data: legendData,
},
grid: {
...defaultGrid,

View File

@@ -188,7 +188,6 @@ export default function transformProps(
legendMargin,
legendOrientation,
legendType,
legendSort,
showLegend,
baseEdgeWidth,
baseNodeSize,
@@ -354,10 +353,7 @@ export default function transformProps(
},
legend: {
...getLegendProps(legendType, legendOrientation, showLegend, theme),
data: categoryList.sort((a: string, b: string) => {
if (!legendSort) return 0;
return legendSort === 'asc' ? a.localeCompare(b) : b.localeCompare(a);
}),
data: categoryList,
},
series,
};

View File

@@ -129,7 +129,6 @@ export default function transformProps(
theme,
inContextMenu,
emitCrossFilters,
legendState,
} = chartProps;
let focusedSeries: string | null = null;
@@ -158,9 +157,7 @@ export default function transformProps(
timeShiftColor,
contributionMode,
legendOrientation,
legendMargin,
legendType,
legendSort,
logAxis,
logAxisSecondary,
markerEnabled,
@@ -426,7 +423,6 @@ export default function transformProps(
{
...entry,
id: `${displayName || ''}`,
name: `${displayName || ''}`,
},
colorScale,
colorScaleKey,
@@ -493,7 +489,6 @@ export default function transformProps(
{
...entry,
id: `${displayName || ''}`,
name: `${displayName || ''}`,
},
colorScale,
@@ -555,7 +550,7 @@ export default function transformProps(
legendOrientation,
addYAxisTitleOffset,
zoomable,
legendMargin,
null,
addXAxisTitleOffset,
yAxisTitlePosition,
convertInteger(yAxisTitleMargin),
@@ -718,8 +713,6 @@ export default function transformProps(
showLegend,
theme,
zoomable,
legendState,
chartPadding,
),
// @ts-ignore
data: series
@@ -729,11 +722,7 @@ export default function transformProps(
ForecastSeriesEnum.Observation,
)
.map(entry => entry.id || entry.name || '')
.concat(extractAnnotationLabels(annotationLayers))
.sort((a: string, b: string) => {
if (!legendSort) return 0;
return legendSort === 'asc' ? a.localeCompare(b) : b.localeCompare(a);
}),
.concat(extractAnnotationLabels(annotationLayers)),
},
series: dedupSeries(reorderForecastSeries(series) as SeriesOption[]),
toolbox: {

View File

@@ -29,7 +29,7 @@ import {
sharedControls,
} from '@superset-ui/chart-controls';
import { DEFAULT_FORM_DATA } from './types';
import { legendSection } from '../controls';
import { legendSection, legendSortControl } from '../controls';
const {
donut,
@@ -119,6 +119,7 @@ const config: ControlPanelConfig = {
},
],
...legendSection,
[legendSortControl],
// eslint-disable-next-line react/jsx-key
[<ControlSubSectionHeader>{t('Labels')}</ControlSubSectionHeader>],
[

View File

@@ -40,6 +40,7 @@ export type EchartsPieFormData = QueryFormData &
labelType: EchartsPieLabelType;
labelTemplate: string | null;
labelsOutside: boolean;
legendSort: 'asc' | 'desc' | null;
metric?: string;
outerRadius: number;
showLabels: boolean;

View File

@@ -116,7 +116,6 @@ export default function transformProps(
dateFormat,
showLabels,
showLegend,
legendSort,
isCircle,
columnConfig,
sliceId,
@@ -355,10 +354,7 @@ export default function transformProps(
},
legend: {
...getLegendProps(legendType, legendOrientation, showLegend, theme),
data: Array.from(columnsLabelMap.keys()).sort((a: string, b: string) => {
if (!legendSort) return 0;
return legendSort === 'asc' ? a.localeCompare(b) : b.localeCompare(a);
}),
data: Array.from(columnsLabelMap.keys()),
},
series,
radar: {

View File

@@ -149,7 +149,6 @@ export default function transformProps(
legendOrientation,
legendType,
legendMargin,
legendSort,
logAxis,
markerEnabled,
markerSize,
@@ -647,10 +646,7 @@ export default function transformProps(
padding,
),
scrollDataIndex: legendIndex || 0,
data: legendData.sort((a: string, b: string) => {
if (!legendSort) return 0;
return legendSort === 'asc' ? a.localeCompare(b) : b.localeCompare(a);
}) as string[],
data: legendData as string[],
},
series: dedupSeries(reorderForecastSeries(series) as SeriesOption[]),
toolbox: {

View File

@@ -103,7 +103,6 @@ export const DEFAULT_LEGEND_FORM_DATA: LegendFormData = {
legendOrientation: LegendOrientation.Top,
legendType: LegendType.Scroll,
showLegend: true,
legendSort: null,
};
export const DEFAULT_TITLE_FORM_DATA: TitleFormData = {

View File

@@ -121,7 +121,6 @@ export const legendSection: ControlSetRow[] = [
[legendTypeControl],
[legendOrientationControl],
[legendMarginControl],
[legendSortControl],
];
export const showValueControl: ControlSetItem = {

View File

@@ -98,7 +98,6 @@ export type LegendFormData = {
legendOrientation: LegendOrientation;
legendType: LegendType;
showLegend: boolean;
legendSort: 'asc' | 'desc' | null;
};
export type EventHandlers = Record<string, { (props: any): void }>;

View File

@@ -27,65 +27,62 @@ import { EchartsBubbleChartProps } from 'plugins/plugin-chart-echarts/src/Bubble
import transformProps, { formatTooltip } from '../../src/Bubble/transformProps';
const defaultFormData: SqlaFormData = {
datasource: '1__table',
viz_type: 'echarts_bubble',
entity: 'customer_name',
x: 'count',
y: {
aggregate: 'sum',
column: {
column_name: 'price_each',
},
expressionType: 'simple',
label: 'SUM(price_each)',
},
size: {
aggregate: 'sum',
column: {
column_name: 'sales',
},
expressionType: 'simple',
label: 'SUM(sales)',
},
xAxisBounds: [null, null],
yAxisBounds: [null, null],
};
const queriesData = [
{
data: [
{
customer_name: 'AV Stores, Co.',
count: 10,
'SUM(price_each)': 20,
'SUM(sales)': 30,
describe('Bubble transformProps', () => {
const defaultFormData: SqlaFormData = {
datasource: '1__table',
viz_type: 'echarts_bubble',
entity: 'customer_name',
x: 'count',
y: {
aggregate: 'sum',
column: {
column_name: 'price_each',
},
{
customer_name: 'Alpha Cognac',
count: 40,
'SUM(price_each)': 50,
'SUM(sales)': 60,
expressionType: 'simple',
label: 'SUM(price_each)',
},
size: {
aggregate: 'sum',
column: {
column_name: 'sales',
},
expressionType: 'simple',
label: 'SUM(sales)',
},
xAxisBounds: [null, null],
yAxisBounds: [null, null],
};
const chartConfig: ChartPropsConfig = {
formData: defaultFormData,
height: 800,
width: 800,
queriesData: [
{
customer_name: 'Amica Models & Co.',
count: 70,
'SUM(price_each)': 80,
'SUM(sales)': 90,
data: [
{
customer_name: 'AV Stores, Co.',
count: 10,
'SUM(price_each)': 20,
'SUM(sales)': 30,
},
{
customer_name: 'Alpha Cognac',
count: 40,
'SUM(price_each)': 50,
'SUM(sales)': 60,
},
{
customer_name: 'Amica Models & Co.',
count: 70,
'SUM(price_each)': 80,
'SUM(sales)': 90,
},
],
},
],
},
];
theme: supersetTheme,
};
const chartConfig: ChartPropsConfig = {
formData: defaultFormData,
height: 800,
width: 800,
queriesData,
theme: supersetTheme,
};
describe('Bubble transformProps', () => {
it('Should transform props for viz', () => {
const chartProps = new ChartProps(chartConfig);
expect(transformProps(chartProps as EchartsBubbleChartProps)).toEqual(
@@ -204,49 +201,3 @@ describe('Bubble formatTooltip', () => {
expect(html).toContain('300.0%');
});
});
describe('legend sorting', () => {
const createChartProps = (overrides = {}) =>
new ChartProps({
...chartConfig,
formData: {
...defaultFormData,
...overrides,
},
});
it('preserves original data order when no sort specified', () => {
const props = createChartProps({ legendSort: null });
const result = transformProps(props as EchartsBubbleChartProps);
const legendData = (result.echartOptions.legend as any).data;
expect(legendData).toEqual([
'AV Stores, Co.',
'Alpha Cognac',
'Amica Models & Co.',
]);
});
it('sorts alphabetically ascending when legendSort is "asc"', () => {
const props = createChartProps({ legendSort: 'asc' });
const result = transformProps(props as EchartsBubbleChartProps);
const legendData = (result.echartOptions.legend as any).data;
expect(legendData).toEqual([
'Alpha Cognac',
'Amica Models & Co.',
'AV Stores, Co.',
]);
});
it('sorts alphabetically descending when legendSort is "desc"', () => {
const props = createChartProps({ legendSort: 'desc' });
const result = transformProps(props as EchartsBubbleChartProps);
const legendData = (result.echartOptions.legend as any).data;
expect(legendData).toEqual([
'AV Stores, Co.',
'Amica Models & Co.',
'Alpha Cognac',
]);
});
});

View File

@@ -27,30 +27,29 @@ import {
PercentCalcType,
} from '../../src/Funnel/types';
const formData = {
colorScheme: 'bnbColors',
datasource: '3__table',
granularity_sqla: 'ds',
metric: 'sum__num',
groupby: ['foo', 'bar'],
};
const queriesData = [
{
data: [
{ foo: 'Sylvester', bar: 1, sum__num: 10 },
{ foo: 'Arnold', bar: 2, sum__num: 2.5 },
],
},
];
const chartProps = new ChartProps({
formData,
width: 800,
height: 600,
queriesData,
theme: supersetTheme,
});
describe('Funnel transformProps', () => {
const formData = {
colorScheme: 'bnbColors',
datasource: '3__table',
granularity_sqla: 'ds',
metric: 'sum__num',
groupby: ['foo', 'bar'],
};
const chartProps = new ChartProps({
formData,
width: 800,
height: 600,
queriesData: [
{
data: [
{ foo: 'Sylvester', bar: 1, sum__num: 10 },
{ foo: 'Arnold', bar: 2, sum__num: 2.5 },
],
},
],
theme: supersetTheme,
});
it('should transform chart props for viz', () => {
expect(transformProps(chartProps as EchartsFunnelChartProps)).toEqual(
expect.objectContaining({
@@ -124,49 +123,3 @@ describe('formatFunnelLabel', () => {
).toEqual(['&lt;NULL&gt;', '1.23k', '12.34%']);
});
});
describe('legend sorting', () => {
const legendQueriesData = [
{
data: [
{ foo: 'Sylvester', sum__num: 10 },
{ foo: 'Arnold', sum__num: 2.5 },
{ foo: 'Mark', sum__num: 13 },
],
},
];
const createChartProps = (overrides = {}) =>
new ChartProps({
...chartProps,
formData: {
...formData,
groupby: ['foo'],
...overrides,
},
queriesData: legendQueriesData,
});
it('preserves original data order when no sort specified', () => {
const props = createChartProps({ legendSort: null });
const result = transformProps(props as EchartsFunnelChartProps);
const legendData = (result.echartOptions.legend as any).data;
expect(legendData).toEqual(['Sylvester', 'Arnold', 'Mark']);
});
it('sorts alphabetically ascending when legendSort is "asc"', () => {
const props = createChartProps({ legendSort: 'asc' });
const result = transformProps(props as EchartsFunnelChartProps);
const legendData = (result.echartOptions.legend as any).data;
expect(legendData).toEqual(['Arnold', 'Mark', 'Sylvester']);
});
it('sorts alphabetically descending when legendSort is "desc"', () => {
const props = createChartProps({ legendSort: 'desc' });
const result = transformProps(props as EchartsFunnelChartProps);
const legendData = (result.echartOptions.legend as any).data;
expect(legendData).toEqual(['Sylvester', 'Mark', 'Arnold']);
});
});

View File

@@ -27,64 +27,63 @@ import {
EchartsGanttFormData,
} from '../../src/Gantt/types';
const formData: EchartsGanttFormData = {
viz_type: 'gantt_chart',
datasource: '1__table',
startTime: 'startTime',
endTime: 'endTime',
yAxis: {
label: 'Y Axis',
sqlExpression: 'y_axis',
expressionType: 'SQL',
},
tooltipMetrics: ['tooltip_metric'],
tooltipColumns: ['tooltip_column'],
series: 'series',
xAxisTimeFormat: '%H:%M',
tooltipTimeFormat: '%H:%M',
tooltipValuesFormat: 'DURATION_SEC',
colorScheme: 'bnbColors',
zoomable: true,
xAxisTitleMargin: undefined,
yAxisTitleMargin: undefined,
xAxisTimeBounds: [null, '19:00:00'],
subcategories: true,
legendMargin: 0,
legendOrientation: LegendOrientation.Top,
legendType: LegendType.Scroll,
showLegend: true,
sortSeriesAscending: true,
legendSort: null,
};
const queriesData = [
{
data: [
{
startTime: Date.UTC(2025, 1, 1, 13, 0, 0),
endTime: Date.UTC(2025, 1, 1, 14, 0, 0),
'Y Axis': 'first',
tooltip_column: 'tooltip value 1',
series: 'series value 1',
},
{
startTime: Date.UTC(2025, 1, 1, 18, 0, 0),
endTime: Date.UTC(2025, 1, 1, 20, 0, 0),
'Y Axis': 'second',
tooltip_column: 'tooltip value 2',
series: 'series value 2',
},
],
colnames: ['startTime', 'endTime', 'Y Axis', 'tooltip_column', 'series'],
},
];
const chartPropsConfig = {
formData,
queriesData,
theme: supersetTheme,
};
describe('Gantt transformProps', () => {
const formData: EchartsGanttFormData = {
viz_type: 'gantt_chart',
datasource: '1__table',
startTime: 'startTime',
endTime: 'endTime',
yAxis: {
label: 'Y Axis',
sqlExpression: 'y_axis',
expressionType: 'SQL',
},
tooltipMetrics: ['tooltip_metric'],
tooltipColumns: ['tooltip_column'],
series: 'series',
xAxisTimeFormat: '%H:%M',
tooltipTimeFormat: '%H:%M',
tooltipValuesFormat: 'DURATION_SEC',
colorScheme: 'bnbColors',
zoomable: true,
xAxisTitleMargin: undefined,
yAxisTitleMargin: undefined,
xAxisTimeBounds: [null, '19:00:00'],
subcategories: true,
legendMargin: 0,
legendOrientation: LegendOrientation.Top,
legendType: LegendType.Scroll,
showLegend: true,
sortSeriesAscending: true,
};
const queriesData = [
{
data: [
{
startTime: Date.UTC(2025, 1, 1, 13, 0, 0),
endTime: Date.UTC(2025, 1, 1, 14, 0, 0),
'Y Axis': 'first',
tooltip_column: 'tooltip value 1',
series: 'series value 1',
},
{
startTime: Date.UTC(2025, 1, 1, 18, 0, 0),
endTime: Date.UTC(2025, 1, 1, 20, 0, 0),
'Y Axis': 'second',
tooltip_column: 'tooltip value 2',
series: 'series value 2',
},
],
colnames: ['startTime', 'endTime', 'Y Axis', 'tooltip_column', 'series'],
},
];
const chartPropsConfig = {
formData,
queriesData,
theme: supersetTheme,
};
it('should transform chart props', () => {
const chartProps = new ChartProps(chartPropsConfig);
const transformedProps = transformProps(
@@ -268,38 +267,3 @@ describe('Gantt transformProps', () => {
});
});
});
describe('legend sorting', () => {
const createChartProps = (overrides = {}) =>
new ChartProps({
...chartPropsConfig,
formData: {
...formData,
...overrides,
},
});
it('preserves original data order when no sort specified', () => {
const props = createChartProps({ legendSort: null });
const result = transformProps(props as EchartsGanttChartProps);
const legendData = (result.echartOptions.legend as any).data;
expect(legendData).toEqual(['series value 1', 'series value 2']);
});
it('sorts alphabetically ascending when legendSort is "asc"', () => {
const props = createChartProps({ legendSort: 'asc' });
const result = transformProps(props as EchartsGanttChartProps);
const legendData = (result.echartOptions.legend as any).data;
expect(legendData).toEqual(['series value 1', 'series value 2']);
});
it('sorts alphabetically descending when legendSort is "desc"', () => {
const props = createChartProps({ legendSort: 'desc' });
const result = transformProps(props as EchartsGanttChartProps);
const legendData = (result.echartOptions.legend as any).data;
expect(legendData).toEqual(['series value 2', 'series value 1']);
});
});

View File

@@ -21,43 +21,43 @@ import transformProps from '../../src/Graph/transformProps';
import { DEFAULT_GRAPH_SERIES_OPTION } from '../../src/Graph/constants';
import { EchartsGraphChartProps } from '../../src/Graph/types';
const formData: SqlaFormData = {
colorScheme: 'bnbColors',
datasource: '3__table',
granularity_sqla: 'ds',
metric: 'count',
source: 'source_column',
target: 'target_column',
category: null,
viz_type: 'graph',
};
const queriesData = [
{
colnames: ['source_column', 'target_column', 'count'],
data: [
{
source_column: 'source_value_1',
target_column: 'target_value_1',
count: 6,
},
{
source_column: 'source_value_2',
target_column: 'target_value_2',
count: 5,
},
],
},
];
const chartPropsConfig = {
formData,
width: 800,
height: 600,
queriesData,
theme: supersetTheme,
};
describe('EchartsGraph transformProps', () => {
it('should transform chart props for viz without category', () => {
const formData: SqlaFormData = {
colorScheme: 'bnbColors',
datasource: '3__table',
granularity_sqla: 'ds',
metric: 'count',
source: 'source_column',
target: 'target_column',
category: null,
viz_type: 'graph',
};
const queriesData = [
{
colnames: ['source_column', 'target_column', 'count'],
data: [
{
source_column: 'source_value_1',
target_column: 'target_value_1',
count: 6,
},
{
source_column: 'source_value_2',
target_column: 'target_value_2',
count: 5,
},
],
},
];
const chartPropsConfig = {
formData,
width: 800,
height: 600,
queriesData,
theme: supersetTheme,
};
const chartProps = new ChartProps(chartPropsConfig);
expect(transformProps(chartProps as EchartsGraphChartProps)).toEqual(
expect.objectContaining({
@@ -263,91 +263,3 @@ describe('EchartsGraph transformProps', () => {
);
});
});
describe('legend sorting', () => {
const queriesData = [
{
colnames: [
'source_column',
'target_column',
'source_category_column',
'target_category_column',
'count',
],
data: [
{
source_column: 'source_value',
target_column: 'target_value',
source_category_column: 'category_value_1',
target_category_column: 'category_value_3',
count: 6,
},
{
source_column: 'source_value',
target_column: 'target_value',
source_category_column: 'category_value_3',
target_category_column: 'category_value_2',
count: 5,
},
{
source_column: 'source_value',
target_column: 'target_value',
source_category_column: 'category_value_2',
target_category_column: 'category_value_1',
count: 4,
},
],
},
];
const getChartProps = (overrides = {}) =>
new ChartProps({
...chartPropsConfig,
formData: {
...formData,
...overrides,
sourceCategory: 'source_category_column',
targetCategory: 'target_category_column',
},
queriesData,
});
it('sort legend by data', () => {
const chartProps = getChartProps({
legendSort: null,
});
const transformed = transformProps(chartProps as EchartsGraphChartProps);
expect((transformed.echartOptions.legend as any).data).toEqual([
'category_value_1',
'category_value_3',
'category_value_2',
]);
});
it('sort legend by label ascending', () => {
const chartProps = getChartProps({
legendSort: 'asc',
});
const transformed = transformProps(chartProps as EchartsGraphChartProps);
expect((transformed.echartOptions.legend as any).data).toEqual([
'category_value_1',
'category_value_2',
'category_value_3',
]);
});
it('sort legend by label descending', () => {
const chartProps = getChartProps({
legendSort: 'desc',
});
const transformed = transformProps(chartProps as EchartsGraphChartProps);
expect((transformed.echartOptions.legend as any).data).toEqual([
'category_value_3',
'category_value_2',
'category_value_1',
]);
});
});

View File

@@ -81,7 +81,6 @@ const formData: EchartsMixedTimeseriesFormData = {
forecastPeriods: [],
forecastInterval: 0,
forecastSeasonalityDaily: 0,
legendSort: null,
};
const queriesData = [
@@ -138,24 +137,6 @@ it('should transform chart props for viz with showQueryIdentifiers=false', () =>
expect(seriesIds).not.toContain('sum__num (Query A), boy');
expect(seriesIds).not.toContain('sum__num (Query B), girl');
expect(seriesIds).not.toContain('sum__num (Query B), boy');
// Check that series name include query identifiers
const seriesName = (transformed.echartOptions.series as any[]).map(
(s: any) => s.name,
);
expect(seriesName).toContain('sum__num, girl');
expect(seriesName).toContain('sum__num, boy');
expect(seriesName).not.toContain('sum__num (Query A), girl');
expect(seriesName).not.toContain('sum__num (Query A), boy');
expect(seriesName).not.toContain('sum__num (Query B), girl');
expect(seriesName).not.toContain('sum__num (Query B), boy');
expect((transformed.echartOptions.legend as any).data).toEqual([
'sum__num, girl',
'sum__num, boy',
'sum__num, girl',
'sum__num, boy',
]);
});
it('should transform chart props for viz with showQueryIdentifiers=true', () => {
@@ -179,145 +160,4 @@ it('should transform chart props for viz with showQueryIdentifiers=true', () =>
expect(seriesIds).toContain('sum__num (Query B), boy');
expect(seriesIds).not.toContain('sum__num, girl');
expect(seriesIds).not.toContain('sum__num, boy');
// Check that series name include query identifiers
const seriesName = (transformed.echartOptions.series as any[]).map(
(s: any) => s.name,
);
expect(seriesName).toContain('sum__num (Query A), girl');
expect(seriesName).toContain('sum__num (Query A), boy');
expect(seriesName).toContain('sum__num (Query B), girl');
expect(seriesName).toContain('sum__num (Query B), boy');
expect(seriesName).not.toContain('sum__num, girl');
expect(seriesName).not.toContain('sum__num, boy');
expect((transformed.echartOptions.legend as any).data).toEqual([
'sum__num (Query A), girl',
'sum__num (Query A), boy',
'sum__num (Query B), girl',
'sum__num (Query B), boy',
]);
});
describe('legend sorting', () => {
const getChartProps = (overrides = {}) =>
new ChartProps({
...chartPropsConfig,
formData: {
...formData,
...overrides,
showQueryIdentifiers: true,
},
});
it('sort legend by data', () => {
const chartProps = getChartProps({
legendSort: null,
});
const transformed = transformProps(
chartProps as EchartsMixedTimeseriesProps,
);
expect((transformed.echartOptions.legend as any).data).toEqual([
'sum__num (Query A), girl',
'sum__num (Query A), boy',
'sum__num (Query B), girl',
'sum__num (Query B), boy',
]);
});
it('sort legend by label ascending', () => {
const chartProps = getChartProps({
legendSort: 'asc',
});
const transformed = transformProps(
chartProps as EchartsMixedTimeseriesProps,
);
expect((transformed.echartOptions.legend as any).data).toEqual([
'sum__num (Query A), boy',
'sum__num (Query A), girl',
'sum__num (Query B), boy',
'sum__num (Query B), girl',
]);
});
it('sort legend by label descending', () => {
const chartProps = getChartProps({
legendSort: 'desc',
});
const transformed = transformProps(
chartProps as EchartsMixedTimeseriesProps,
);
expect((transformed.echartOptions.legend as any).data).toEqual([
'sum__num (Query B), girl',
'sum__num (Query B), boy',
'sum__num (Query A), girl',
'sum__num (Query A), boy',
]);
});
});
it('legend margin: top orientation sets grid.top correctly', () => {
const chartPropsConfigWithoutIdentifiers = {
...chartPropsConfig,
formData: {
...formData,
legendMargin: 250,
showLegend: true,
},
};
const chartProps = new ChartProps(chartPropsConfigWithoutIdentifiers);
const transformed = transformProps(chartProps as EchartsMixedTimeseriesProps);
expect((transformed.echartOptions.grid as any).top).toEqual(270);
});
it('legend margin: bottom orientation sets grid.bottom correctly', () => {
const chartPropsConfigWithoutIdentifiers = {
...chartPropsConfig,
formData: {
...formData,
legendMargin: 250,
showLegend: true,
legendOrientation: LegendOrientation.Bottom,
},
};
const chartProps = new ChartProps(chartPropsConfigWithoutIdentifiers);
const transformed = transformProps(chartProps as EchartsMixedTimeseriesProps);
expect((transformed.echartOptions.grid as any).bottom).toEqual(270);
});
it('legend margin: left orientation sets grid.left correctly', () => {
const chartPropsConfigWithoutIdentifiers = {
...chartPropsConfig,
formData: {
...formData,
legendMargin: 250,
showLegend: true,
legendOrientation: LegendOrientation.Left,
},
};
const chartProps = new ChartProps(chartPropsConfigWithoutIdentifiers);
const transformed = transformProps(chartProps as EchartsMixedTimeseriesProps);
expect((transformed.echartOptions.grid as any).left).toEqual(270);
});
it('legend margin: right orientation sets grid.right correctly', () => {
const chartPropsConfigWithoutIdentifiers = {
...chartPropsConfig,
formData: {
...formData,
legendMargin: 270,
showLegend: true,
legendOrientation: LegendOrientation.Right,
},
};
const chartProps = new ChartProps(chartPropsConfigWithoutIdentifiers);
const transformed = transformProps(chartProps as EchartsMixedTimeseriesProps);
expect((transformed.echartOptions.grid as any).right).toEqual(270);
});

View File

@@ -446,7 +446,7 @@ describe('Other category', () => {
});
});
describe('legend sorting', () => {
describe('Sort Legend', () => {
const defaultFormData: SqlaFormData = {
colorScheme: 'bnbColors',
datasource: '3__table',

View File

@@ -42,56 +42,54 @@ interface RadarSeriesData {
name: string;
}
const formData: Partial<EchartsRadarFormData> = {
colorScheme: 'supersetColors',
datasource: '3__table',
granularity_sqla: 'ds',
columnConfig: {
'MAX(na_sales)': {
radarMetricMaxValue: null,
radarMetricMinValue: 0,
describe('Radar transformProps', () => {
const formData: Partial<EchartsRadarFormData> = {
colorScheme: 'supersetColors',
datasource: '3__table',
granularity_sqla: 'ds',
columnConfig: {
'MAX(na_sales)': {
radarMetricMaxValue: null,
radarMetricMinValue: 0,
},
'SUM(eu_sales)': {
radarMetricMaxValue: 5000,
},
},
'SUM(eu_sales)': {
radarMetricMaxValue: 5000,
},
},
groupby: [],
metrics: [
'MAX(na_sales)',
'SUM(jp_sales)',
'SUM(other_sales)',
'SUM(eu_sales)',
],
viz_type: 'radar',
numberFormat: 'SMART_NUMBER',
dateFormat: 'smart_date',
showLegend: true,
showLabels: true,
isCircle: false,
};
groupby: [],
metrics: [
'MAX(na_sales)',
'SUM(jp_sales)',
'SUM(other_sales)',
'SUM(eu_sales)',
],
viz_type: 'radar',
numberFormat: 'SMART_NUMBER',
dateFormat: 'smart_date',
showLegend: true,
showLabels: true,
isCircle: false,
};
const queriesData = [
{
data: [
const chartProps = new ChartProps({
formData,
width: 800,
height: 600,
queriesData: [
{
'MAX(na_sales)': 41.49,
'SUM(jp_sales)': 1290.99,
'SUM(other_sales)': 797.73,
'SUM(eu_sales)': 2434.13,
data: [
{
'MAX(na_sales)': 41.49,
'SUM(jp_sales)': 1290.99,
'SUM(other_sales)': 797.73,
'SUM(eu_sales)': 2434.13,
},
],
},
],
},
];
theme: supersetTheme,
});
const chartProps = new ChartProps({
formData,
width: 800,
height: 600,
queriesData,
theme: supersetTheme,
});
describe('Radar transformProps', () => {
it('should transform chart props for normalized radar chart & normalize all metrics except the ones with custom min & max', () => {
const transformedProps = transformProps(
chartProps as EchartsRadarChartProps,
@@ -127,77 +125,3 @@ describe('Radar transformProps', () => {
]);
});
});
describe('legend sorting', () => {
const legendSortData = [
{
data: [
{
name: 'Sylvester sales',
'SUM(jp_sales)': 1290.99,
'SUM(other_sales)': 797.73,
'SUM(eu_sales)': 2434.13,
},
{
name: 'Arnold sales',
'SUM(jp_sales)': 290.99,
'SUM(other_sales)': 627.73,
'SUM(eu_sales)': 434.13,
},
{
name: 'Mark sales',
'SUM(jp_sales)': 2290.99,
'SUM(other_sales)': 1297.73,
'SUM(eu_sales)': 934.13,
},
],
},
];
const createChartProps = (overrides = {}) =>
new ChartProps({
...chartProps,
formData: {
...formData,
groupby: ['name'],
metrics: ['SUM(jp_sales)', 'SUM(other_sales)', 'SUM(eu_sales)'],
...overrides,
},
queriesData: legendSortData,
});
it('preserves original data order when no sort specified', () => {
const props = createChartProps({ legendSort: null });
const result = transformProps(props as EchartsRadarChartProps);
const legendData = (result.echartOptions.legend as any).data;
expect(legendData).toEqual([
'Sylvester sales',
'Arnold sales',
'Mark sales',
]);
});
it('sorts alphabetically ascending when legendSort is "asc"', () => {
const props = createChartProps({ legendSort: 'asc' });
const result = transformProps(props as EchartsRadarChartProps);
const legendData = (result.echartOptions.legend as any).data;
expect(legendData).toEqual([
'Arnold sales',
'Mark sales',
'Sylvester sales',
]);
});
it('sorts alphabetically descending when legendSort is "desc"', () => {
const props = createChartProps({ legendSort: 'desc' });
const result = transformProps(props as EchartsRadarChartProps);
const legendData = (result.echartOptions.legend as any).data;
expect(legendData).toEqual([
'Sylvester sales',
'Mark sales',
'Arnold sales',
]);
});
});

View File

@@ -31,31 +31,31 @@ import {
import { EchartsTimeseriesChartProps } from '../../src/types';
import transformProps from '../../src/Timeseries/transformProps';
const formData: SqlaFormData = {
colorScheme: 'bnbColors',
datasource: '3__table',
granularity_sqla: 'ds',
metric: 'sum__num',
groupby: ['foo', 'bar'],
viz_type: 'my_viz',
};
const queriesData = [
{
data: [
{ 'San Francisco': 1, 'New York': 2, __timestamp: 599616000000 },
{ 'San Francisco': 3, 'New York': 4, __timestamp: 599916000000 },
],
},
];
const chartPropsConfig = {
formData,
width: 800,
height: 600,
queriesData,
theme: supersetTheme,
};
describe('EchartsTimeseries transformProps', () => {
const formData: SqlaFormData = {
colorScheme: 'bnbColors',
datasource: '3__table',
granularity_sqla: 'ds',
metric: 'sum__num',
groupby: ['foo', 'bar'],
viz_type: 'my_viz',
};
const queriesData = [
{
data: [
{ 'San Francisco': 1, 'New York': 2, __timestamp: 599616000000 },
{ 'San Francisco': 3, 'New York': 4, __timestamp: 599916000000 },
],
},
];
const chartPropsConfig = {
formData,
width: 800,
height: 600,
queriesData,
theme: supersetTheme,
};
it('should transform chart props for viz', () => {
const chartProps = new ChartProps(chartPropsConfig);
expect(transformProps(chartProps as EchartsTimeseriesChartProps)).toEqual(
@@ -625,101 +625,3 @@ describe('Does transformProps transform series correctly', () => {
});
});
});
describe('legend sorting', () => {
const legendSortData = [
{
data: [
{
Milton: 40,
'San Francisco': 1,
'New York': 2,
Boston: 1,
__timestamp: 599616000000,
},
{
Milton: 20,
'San Francisco': 3,
'New York': 4,
Boston: 1,
__timestamp: 599916000000,
},
{
Milton: 60,
'San Francisco': 5,
'New York': 8,
Boston: 6,
__timestamp: 600216000000,
},
{
Milton: 10,
'San Francisco': 2,
'New York': 7,
Boston: 2,
__timestamp: 600516000000,
},
],
},
];
const getChartProps = (formData: Partial<SqlaFormData>) =>
new ChartProps({
...chartPropsConfig,
formData: { ...formData },
queriesData: legendSortData,
});
it('sort legend by data', () => {
const chartProps = getChartProps({
legendSort: null,
sortSeriesType: 'min',
sortSeriesAscending: true,
});
const transformed = transformProps(
chartProps as EchartsTimeseriesChartProps,
);
expect((transformed.echartOptions.legend as any).data).toEqual([
'San Francisco',
'Boston',
'New York',
'Milton',
]);
});
it('sort legend by label ascending', () => {
const chartProps = getChartProps({
legendSort: 'asc',
sortSeriesType: 'min',
sortSeriesAscending: true,
});
const transformed = transformProps(
chartProps as EchartsTimeseriesChartProps,
);
expect((transformed.echartOptions.legend as any).data).toEqual([
'Boston',
'Milton',
'New York',
'San Francisco',
]);
});
it('sort legend by label descending', () => {
const chartProps = getChartProps({
legendSort: 'desc',
sortSeriesType: 'min',
sortSeriesAscending: true,
});
const transformed = transformProps(
chartProps as EchartsTimeseriesChartProps,
);
expect((transformed.echartOptions.legend as any).data).toEqual([
'San Francisco',
'New York',
'Milton',
'Boston',
]);
});
});

View File

@@ -73,11 +73,14 @@ beforeEach(() => {
dbId: expectDbId,
forceRefresh: false,
},
fakeSchemaApiResult.map(value => ({
value,
label: value,
title: value,
})),
{
result: fakeSchemaApiResult.map(value => ({
value,
label: value,
title: value,
})),
default: null,
},
),
);
store.dispatch(
@@ -307,11 +310,14 @@ test('returns long keywords with docText', async () => {
dbId: expectLongKeywordDbId,
forceRefresh: false,
},
['short', longKeyword].map(value => ({
value,
label: value,
title: value,
})),
{
result: ['short', longKeyword].map(value => ({
value,
label: value,
title: value,
})),
default: null,
},
),
);
});

View File

@@ -164,7 +164,7 @@ export function useKeywords(
const schemaKeywords = useMemo(
() =>
(schemaOptions ?? []).map(s => ({
(schemaOptions?.result ?? []).map(s => ({
name: s.label,
value: s.value,
score: SCHEMA_AUTOCOMPLETE_SCORE,

View File

@@ -163,11 +163,13 @@ const fakeDatabaseApiResultInReverseOrder = {
const fakeSchemaApiResult = {
count: 2,
result: ['information_schema', 'public'],
default: 'public',
};
const fakeCatalogApiResult = {
count: 0,
result: [],
default: null,
};
const fakeFunctionNamesApiResult = {
@@ -382,3 +384,143 @@ test('Sends the correct schema when changing the schema', async () => {
);
expect(props.onSchemaChange).toHaveBeenCalledTimes(1);
});
test('Should auto-select default schema on load', async () => {
const props = createProps();
// Remove initial schema to test auto-selection
const propsWithoutSchema = { ...props, schema: undefined };
render(<DatabaseSelector {...propsWithoutSchema} />, {
useRedux: true,
store,
});
// Wait for the default schema to be auto-selected
await waitFor(() => {
expect(propsWithoutSchema.onSchemaChange).toHaveBeenCalledWith('public');
});
});
test('Should auto-select default catalog for multi-catalog databases', async () => {
const multiCatalogApiResult = {
count: 2,
result: ['catalog1', 'catalog2'],
default: 'catalog1',
};
fetchMock.get(catalogApiRoute, multiCatalogApiResult, {
overwriteRoutes: true,
});
const props = createProps();
const multiCatalogDb = {
...props.db!,
allow_multi_catalog: true,
};
const multiCatalogProps = {
...props,
db: multiCatalogDb,
catalog: undefined,
onCatalogChange: jest.fn(),
};
render(<DatabaseSelector {...multiCatalogProps} />, {
useRedux: true,
store,
});
// Wait for the default catalog to be auto-selected
await waitFor(() => {
expect(multiCatalogProps.onCatalogChange).toHaveBeenCalledWith('catalog1');
});
});
test('Should disable schema dropdown while catalogs are loading', async () => {
const props = createProps();
const multiCatalogDb = {
...props.db!,
allow_multi_catalog: true,
};
const multiCatalogProps = {
...props,
db: multiCatalogDb,
catalog: undefined,
};
// Mock a delayed catalog response
fetchMock.get(
catalogApiRoute,
new Promise(resolve =>
setTimeout(
() =>
resolve({
count: 1,
result: ['default_catalog'],
default: 'default_catalog',
}),
100,
),
),
{ overwriteRoutes: true },
);
render(<DatabaseSelector {...multiCatalogProps} />, {
useRedux: true,
store,
});
// Initially, schema dropdown should be disabled while catalogs load
const schemaSelect = screen.getByRole('combobox', {
name: /select schema or type to search schemas/i,
});
expect(schemaSelect).toBeDisabled();
// Wait for catalogs to load and schema dropdown to be enabled
await waitFor(
() => {
expect(schemaSelect).toBeEnabled();
},
{ timeout: 200 },
);
});
test('Should not fetch schemas until catalog is selected for multi-catalog databases', async () => {
const props = createProps();
const multiCatalogDb = {
...props.db!,
allow_multi_catalog: true,
};
const multiCatalogProps = {
...props,
db: multiCatalogDb,
catalog: undefined,
};
render(<DatabaseSelector {...multiCatalogProps} />, {
useRedux: true,
store,
});
// Initially, schemas should not be fetched
expect(fetchMock.calls(schemaApiRoute).length).toBe(0);
// Wait a bit to ensure schemas are still not fetched
await new Promise(resolve => setTimeout(resolve, 100));
expect(fetchMock.calls(schemaApiRoute).length).toBe(0);
});
test('Should fetch schemas immediately for non-catalog databases', async () => {
const props = createProps();
// Non-catalog database (allow_multi_catalog is not set)
render(<DatabaseSelector {...props} />, {
useRedux: true,
store,
});
// Schemas should be fetched immediately for non-catalog databases
await waitFor(() => {
expect(fetchMock.calls(schemaApiRoute).length).toBe(1);
});
});

View File

@@ -238,23 +238,20 @@ export function DatabaseSelector({
}
}
const shouldFetchSchemas =
currentDb?.value &&
(!showCatalogSelector || (currentCatalog && currentCatalog.value));
const {
currentData: schemaData,
isFetching: loadingSchemas,
refetch: refetchSchemas,
} = useSchemas({
dbId: currentDb?.value,
dbId: shouldFetchSchemas ? currentDb?.value : undefined,
catalog: currentCatalog?.value,
onSuccess: (schemas, isFetched) => {
onSuccess: (schemas, isFetched, defaultValue) => {
setErrorPayload(null);
if (schemas.length === 1) {
changeSchema(schemas[0]);
} else if (
!schemas.find(schemaOption => schemaRef.current === schemaOption.value)
) {
changeSchema(undefined);
}
autoSelectSchema(schemas, defaultValue);
if (isFetched) {
addSuccessToast('List refreshed');
}
@@ -268,11 +265,13 @@ export function DatabaseSelector({
},
});
const schemaOptions = schemaData || EMPTY_SCHEMA_OPTIONS;
const schemaOptions = schemaData?.result || EMPTY_SCHEMA_OPTIONS;
function changeCatalog(catalog: CatalogOption | null | undefined) {
setCurrentCatalog(catalog);
setCurrentSchema(undefined);
// Clear schema ref so auto-selection works for the new catalog
schemaRef.current = undefined;
if (onCatalogChange && catalog?.value !== catalogRef.current) {
onCatalogChange(catalog?.value);
}
@@ -284,20 +283,9 @@ export function DatabaseSelector({
refetch: refetchCatalogs,
} = useCatalogs({
dbId: showCatalogSelector ? currentDb?.value : undefined,
onSuccess: (catalogs, isFetched) => {
onSuccess: (catalogs, isFetched, defaultValue) => {
setErrorPayload(null);
if (!showCatalogSelector) {
changeCatalog(null);
} else if (catalogs.length === 1) {
changeCatalog(catalogs[0]);
} else if (
!catalogs.find(
catalogOption => catalogRef.current === catalogOption.value,
)
) {
changeCatalog(undefined);
}
autoSelectCatalog(catalogs, defaultValue);
if (showCatalogSelector && isFetched) {
addSuccessToast('List refreshed');
}
@@ -313,7 +301,59 @@ export function DatabaseSelector({
},
});
const catalogOptions = catalogData || EMPTY_CATALOG_OPTIONS;
const catalogOptions = catalogData?.result || EMPTY_CATALOG_OPTIONS;
// Centralized auto-selection logic
const autoSelectCatalog = useCallback(
(catalogs: CatalogOption[], defaultValue?: string | null) => {
if (!showCatalogSelector) {
changeCatalog(null);
} else if (defaultValue && !catalogRef.current) {
const defaultCatalog = catalogs.find(
catalog => catalog.value === defaultValue,
);
if (defaultCatalog) {
changeCatalog(defaultCatalog);
}
} else if (catalogs.length === 1) {
changeCatalog(catalogs[0]);
} else if (
!catalogs.find(
catalogOption => catalogRef.current === catalogOption.value,
)
) {
changeCatalog(undefined);
}
},
[showCatalogSelector, changeCatalog, catalogRef],
);
const autoSelectSchema = useCallback(
(schemas: SchemaOption[], defaultValue?: string | null) => {
if (defaultValue && !schemaRef.current) {
const defaultSchema = schemas.find(
schema => schema.value === defaultValue,
);
if (defaultSchema) {
changeSchema(defaultSchema);
}
} else if (schemas.length === 1) {
changeSchema(schemas[0]);
} else if (
!schemas.find(schemaOption => schemaRef.current === schemaOption.value)
) {
changeSchema(undefined);
}
},
[schemaRef],
);
// For non-catalog databases, set catalog to null immediately
useEffect(() => {
if (currentDb && !showCatalogSelector) {
setCurrentCatalog(null);
}
}, [currentDb?.id, showCatalogSelector]);
function changeDatabase(
value: { label: string; value: number },
@@ -325,6 +365,9 @@ export function DatabaseSelector({
setCurrentDb(databaseWithId);
setCurrentCatalog(undefined);
setCurrentSchema(undefined);
// Clear refs so auto-selection works when switching back to a database
catalogRef.current = undefined;
schemaRef.current = undefined;
if (onDbChange) {
onDbChange(databaseWithId);
}
@@ -402,7 +445,11 @@ export function DatabaseSelector({
return renderSelectRow(
<Select
ariaLabel={t('Select schema or type to search schemas')}
disabled={!currentDb || readOnly}
disabled={
!currentDb ||
readOnly ||
(showCatalogSelector && (loadingCatalogs || !currentCatalog))
}
header={<FormLabel>{t('Schema')}</FormLabel>}
labelInValue
loading={loadingSchemas}

View File

@@ -80,11 +80,6 @@ import {
getDynamicLabelsColors,
} from '../../utils/colorScheme';
export const TOGGLE_NATIVE_FILTERS_BAR = 'TOGGLE_NATIVE_FILTERS_BAR';
export function toggleNativeFiltersBar(isOpen) {
return { type: TOGGLE_NATIVE_FILTERS_BAR, isOpen };
}
export const SET_UNSAVED_CHANGES = 'SET_UNSAVED_CHANGES';
export function setUnsavedChanges(hasUnsavedChanges) {
return { type: SET_UNSAVED_CHANGES, payload: { hasUnsavedChanges } };

View File

@@ -25,14 +25,7 @@ jest.mock('src/dashboard/containers/SliceAdder', () => () => (
));
test('BuilderComponentPane has correct tabs in correct order', () => {
render(<BuilderComponentPane topOffset={115} />, {
useRedux: true,
initialState: {
dashboardState: {
nativeFiltersBarOpen: false,
},
},
});
render(<BuilderComponentPane topOffset={115} />);
const tabs = screen.getAllByRole('tab');
expect(tabs).toHaveLength(2);
expect(tabs[0]).toHaveTextContent('Charts');

View File

@@ -19,11 +19,9 @@
/* eslint-env browser */
import { rgba } from 'emotion-rgba';
import Tabs from '@superset-ui/core/components/Tabs';
import { t, css, SupersetTheme, useTheme } from '@superset-ui/core';
import { useSelector } from 'react-redux';
import { t, css, SupersetTheme } from '@superset-ui/core';
import SliceAdder from 'src/dashboard/containers/SliceAdder';
import dashboardComponents from 'src/visualizations/presets/dashboardComponents';
import { useMemo } from 'react';
import NewColumn from '../gridComponents/new/NewColumn';
import NewDivider from '../gridComponents/new/NewDivider';
import NewHeader from '../gridComponents/new/NewHeader';
@@ -39,97 +37,81 @@ const TABS_KEYS = {
LAYOUT_ELEMENTS: 'LAYOUT_ELEMENTS',
};
const BuilderComponentPane = ({ topOffset = 0 }) => {
const theme = useTheme();
const nativeFiltersBarOpen = useSelector(
(state: any) => state.dashboardState.nativeFiltersBarOpen ?? false,
);
const tabBarStyle = useMemo(
() => ({
paddingLeft: nativeFiltersBarOpen ? 0 : theme.sizeUnit * 4,
}),
[nativeFiltersBarOpen, theme.sizeUnit],
);
return (
const BuilderComponentPane = ({ topOffset = 0 }) => (
<div
data-test="dashboard-builder-sidepane"
css={css`
position: sticky;
right: 0;
top: ${topOffset}px;
height: calc(100vh - ${topOffset}px);
width: ${BUILDER_PANE_WIDTH}px;
`}
>
<div
data-test="dashboard-builder-sidepane"
css={css`
position: sticky;
right: 0;
top: ${topOffset}px;
height: calc(100vh - ${topOffset}px);
css={(theme: SupersetTheme) => css`
position: absolute;
height: 100%;
width: ${BUILDER_PANE_WIDTH}px;
box-shadow: -4px 0 4px 0 ${rgba(theme.colorBorder, 0.1)};
background-color: ${theme.colorBgBase};
`}
>
<div
<Tabs
data-test="dashboard-builder-component-pane-tabs-navigation"
id="tabs"
css={(theme: SupersetTheme) => css`
position: absolute;
line-height: inherit;
margin-top: ${theme.sizeUnit * 2}px;
height: 100%;
width: ${BUILDER_PANE_WIDTH}px;
box-shadow: -4px 0 4px 0 ${rgba(theme.colorBorder, 0.1)};
background-color: ${theme.colorBgBase};
`}
>
<Tabs
data-test="dashboard-builder-component-pane-tabs-navigation"
id="tabs"
tabBarStyle={tabBarStyle}
css={(theme: SupersetTheme) => css`
line-height: inherit;
margin-top: ${theme.sizeUnit * 2}px;
height: 100%;
& .ant-tabs-content-holder {
& .ant-tabs-content-holder {
height: 100%;
& .ant-tabs-content {
height: 100%;
& .ant-tabs-content {
height: 100%;
}
}
`}
items={[
{
key: TABS_KEYS.CHARTS,
label: t('Charts'),
children: (
<div
css={css`
height: calc(100vh - ${topOffset * 2}px);
`}
>
<SliceAdder />
</div>
),
},
{
key: TABS_KEYS.LAYOUT_ELEMENTS,
label: t('Layout elements'),
children: (
<>
<NewTabs />
<NewRow />
<NewColumn />
<NewHeader />
<NewMarkdown />
<NewDivider />
{dashboardComponents
.getAll()
.map(({ key: componentKey, metadata }) => (
<NewDynamicComponent
key={componentKey}
metadata={metadata}
componentKey={componentKey}
/>
))}
</>
),
},
]}
/>
</div>
}
`}
items={[
{
key: TABS_KEYS.CHARTS,
label: t('Charts'),
children: (
<div
css={css`
height: calc(100vh - ${topOffset * 2}px);
`}
>
<SliceAdder />
</div>
),
},
{
key: TABS_KEYS.LAYOUT_ELEMENTS,
label: t('Layout elements'),
children: (
<>
<NewTabs />
<NewRow />
<NewColumn />
<NewHeader />
<NewMarkdown />
<NewDivider />
{dashboardComponents
.getAll()
.map(({ key: componentKey, metadata }) => (
<NewDynamicComponent
metadata={metadata}
componentKey={componentKey}
/>
))}
</>
),
},
]}
/>
</div>
);
};
</div>
);
export default BuilderComponentPane;

View File

@@ -80,7 +80,6 @@ import DashboardWrapper from './DashboardWrapper';
// @z-index-above-dashboard-charts + 1 = 11
const FiltersPanel = styled.div<{ width: number; hidden: boolean }>`
background-color: ${({ theme }) => theme.colorBgContainer};
grid-column: 1;
grid-row: 1 / span 2;
z-index: 11;
@@ -276,7 +275,6 @@ const StyledDashboardContent = styled.div<{
marginLeft: number;
}>`
${({ theme, editMode, marginLeft }) => css`
background-color: ${theme.colorBgLayout};
display: flex;
flex-direction: row;
flex-wrap: nowrap;
@@ -293,7 +291,9 @@ const StyledDashboardContent = styled.div<{
width: 0;
flex: 1;
position: relative;
margin: ${theme.sizeUnit * 4}px;
margin-top: ${theme.sizeUnit * 4}px;
margin-right: ${theme.sizeUnit * 8}px;
margin-bottom: ${theme.sizeUnit * 4}px;
margin-left: ${marginLeft}px;
${editMode &&
@@ -557,9 +557,13 @@ const DashboardBuilder = () => {
],
);
const dashboardContentMarginLeft = !editMode
? theme.sizeUnit * 4
: theme.sizeUnit * 8;
const dashboardContentMarginLeft =
!dashboardFiltersOpen &&
!editMode &&
nativeFiltersEnabled &&
filterBarOrientation !== FilterBarOrientation.Horizontal
? 0
: theme.sizeUnit * 8;
const renderChild = useCallback(
adjustedWidth => {

View File

@@ -70,12 +70,13 @@ type DashboardContainerProps = {
topLevelTabs?: LayoutItem;
};
export const renderedChartIdsSelector: (state: RootState) => number[] =
createSelector([(state: RootState) => state.charts], charts =>
export const renderedChartIdsSelector = createSelector(
[(state: RootState) => state.charts],
charts =>
Object.values(charts)
.filter(chart => chart.chartStatus === 'rendered')
.map(chart => chart.id),
);
);
const useRenderedChartIds = () => {
const renderedChartIds = useSelector<RootState, number[]>(
@@ -296,7 +297,6 @@ const DashboardContainer: FC<DashboardContainerProps> = ({ topLevelTabs }) => {
allowOverflow
onFocus={handleFocus}
items={tabItems}
tabBarStyle={{ paddingLeft: 0 }}
/>
);
},

View File

@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import { FC, PropsWithChildren, useEffect, useState } from 'react';
import { FC, useEffect, useState } from 'react';
import { css, styled } from '@superset-ui/core';
import { Constants } from '@superset-ui/core/components';
@@ -113,7 +113,9 @@ const StyledDiv = styled.div`
`}
`;
const DashboardWrapper: FC<PropsWithChildren<{}>> = ({ children }) => {
type Props = {};
const DashboardWrapper: FC<Props> = ({ children }) => {
const editMode = useSelector<RootState, boolean>(
state => state.dashboardState.editMode,
);

View File

@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import { useSelector, useDispatch } from 'react-redux';
import { useSelector } from 'react-redux';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { URL_PARAMS } from 'src/constants';
import { getUrlParam } from 'src/utils/urlUtils';
@@ -26,26 +26,23 @@ import {
useFilters,
useNativeFiltersDataMask,
} from '../nativeFilters/FilterBar/state';
import { toggleNativeFiltersBar } from '../../actions/dashboardState';
// eslint-disable-next-line import/prefer-default-export
export const useNativeFilters = () => {
const dispatch = useDispatch();
const [isInitialized, setIsInitialized] = useState(false);
const showNativeFilters = useSelector<RootState, boolean>(
() => getUrlParam(URL_PARAMS.showFilters) ?? true,
);
const canEdit = useSelector<RootState, boolean>(
({ dashboardInfo }) => dashboardInfo.dash_edit_perm,
);
const dashboardFiltersOpen = useSelector<RootState, boolean>(
state => state.dashboardState.nativeFiltersBarOpen ?? false,
);
const filters = useFilters();
const filterValues = useMemo(() => Object.values(filters), [filters]);
const expandFilters = getUrlParam(URL_PARAMS.expandFilters);
const [dashboardFiltersOpen, setDashboardFiltersOpen] = useState(
expandFilters ?? !!filterValues.length,
);
const nativeFiltersEnabled =
showNativeFilters && (canEdit || (!canEdit && filterValues.length !== 0));
@@ -69,13 +66,9 @@ export const useNativeFilters = () => {
!nativeFiltersEnabled ||
missingInitialFilters.length === 0;
const toggleDashboardFiltersOpen = useCallback(
(visible?: boolean) => {
const newState = visible ?? !dashboardFiltersOpen;
dispatch(toggleNativeFiltersBar(newState));
},
[dispatch, dashboardFiltersOpen],
);
const toggleDashboardFiltersOpen = useCallback((visible?: boolean) => {
setDashboardFiltersOpen(prevState => visible ?? !prevState);
}, []);
useEffect(() => {
if (
@@ -84,11 +77,11 @@ export const useNativeFilters = () => {
expandFilters === false ||
(filterValues.length === 0 && nativeFiltersEnabled)
) {
dispatch(toggleNativeFiltersBar(false));
toggleDashboardFiltersOpen(false);
} else {
dispatch(toggleNativeFiltersBar(true));
toggleDashboardFiltersOpen(true);
}
}, [dispatch, filterValues.length, expandFilters, nativeFiltersEnabled]);
}, [filterValues.length]);
useEffect(() => {
if (showDashboard) {

View File

@@ -39,26 +39,26 @@ import { URL_PARAMS } from 'src/constants';
import { enforceSharedLabelsColorsArray } from 'src/utils/colorScheme';
import exportPivotExcel from 'src/utils/downloadAsPivotExcel';
import SliceHeader from '../../SliceHeader';
import MissingChart from '../../MissingChart';
import SliceHeader from '../SliceHeader';
import MissingChart from '../MissingChart';
import {
addDangerToast,
addSuccessToast,
} from '../../../../components/MessageToasts/actions';
} from '../../../components/MessageToasts/actions';
import {
setFocusedFilterField,
toggleExpandSlice,
unsetFocusedFilterField,
} from '../../../actions/dashboardState';
import { changeFilter } from '../../../actions/dashboardFilters';
import { refreshChart } from '../../../../components/Chart/chartAction';
import { logEvent } from '../../../../logger/actions';
} from '../../actions/dashboardState';
import { changeFilter } from '../../actions/dashboardFilters';
import { refreshChart } from '../../../components/Chart/chartAction';
import { logEvent } from '../../../logger/actions';
import {
getActiveFilters,
getAppliedFilterValues,
} from '../../../util/activeDashboardFilters';
import getFormDataWithExtraFilters from '../../../util/charts/getFormDataWithExtraFilters';
import { PLACEHOLDER_DATASOURCE } from '../../../constants';
} from '../../util/activeDashboardFilters';
import getFormDataWithExtraFilters from '../../util/charts/getFormDataWithExtraFilters';
import { PLACEHOLDER_DATASOURCE } from '../../constants';
const propTypes = {
id: PropTypes.number.isRequired,

View File

@@ -20,13 +20,13 @@ import { fireEvent, render } from 'spec/helpers/testing-library';
import { FeatureFlag, VizType } from '@superset-ui/core';
import * as redux from 'redux';
import Chart from 'src/dashboard/components/gridComponents/Chart';
import * as exploreUtils from 'src/explore/exploreUtils';
import { sliceEntitiesForChart as sliceEntities } from 'spec/fixtures/mockSliceEntities';
import mockDatasource from 'spec/fixtures/mockDatasource';
import chartQueries, {
sliceId as queryId,
} from 'spec/fixtures/mockChartQueries';
import Chart from './Chart';
const props = {
id: queryId,

View File

@@ -1,21 +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 Chart from './Chart';
export default Chart;

View File

@@ -35,13 +35,9 @@ import { nativeFiltersInfo } from 'src/dashboard/fixtures/mockNativeFilters';
import newComponentFactory from 'src/dashboard/util/newComponentFactory';
import { initialState } from 'src/SqlLab/fixtures';
import { SET_DIRECT_PATH } from 'src/dashboard/actions/dashboardState';
import {
CHART_TYPE,
COLUMN_TYPE,
ROW_TYPE,
} from '../../../util/componentTypes';
import { CHART_TYPE, COLUMN_TYPE, ROW_TYPE } from '../../util/componentTypes';
import ChartHolder, { CHART_MARGIN } from './ChartHolder';
import { GRID_BASE_UNIT, GRID_GUTTER_SIZE } from '../../../util/constants';
import { GRID_BASE_UNIT, GRID_GUTTER_SIZE } from '../../util/constants';
const DEFAULT_HEADER_HEIGHT = 22;

View File

@@ -1,19 +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.
*/
export { default } from './ChartHolder';

View File

@@ -19,12 +19,12 @@
import { fireEvent, render } from 'spec/helpers/testing-library';
import BackgroundStyleDropdown from 'src/dashboard/components/menu/BackgroundStyleDropdown';
import Column from 'src/dashboard/components/gridComponents/Column';
import IconButton from 'src/dashboard/components/IconButton';
import { getMockStore } from 'spec/fixtures/mockStore';
import { dashboardLayout as mockLayout } from 'spec/fixtures/mockDashboardLayout';
import { initialState } from 'src/SqlLab/fixtures';
import Column from './Column';
jest.mock('src/dashboard/components/dnd/DragDroppable', () => ({
Draggable: ({ children }) => (

View File

@@ -1,21 +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 Column from './Column';
export default Column;

View File

@@ -20,10 +20,10 @@ import { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { css, styled } from '@superset-ui/core';
import { Draggable } from '../../dnd/DragDroppable';
import HoverMenu from '../../menu/HoverMenu';
import DeleteComponentButton from '../../DeleteComponentButton';
import { componentShape } from '../../../util/propShapes';
import { Draggable } from '../dnd/DragDroppable';
import HoverMenu from '../menu/HoverMenu';
import DeleteComponentButton from '../DeleteComponentButton';
import { componentShape } from '../../util/propShapes';
const propTypes = {
id: PropTypes.string.isRequired,

View File

@@ -18,13 +18,13 @@
*/
import sinon from 'sinon';
import Divider from 'src/dashboard/components/gridComponents/Divider';
import newComponentFactory from 'src/dashboard/util/newComponentFactory';
import {
DIVIDER_TYPE,
DASHBOARD_GRID_TYPE,
} from 'src/dashboard/util/componentTypes';
import { screen, render, userEvent } from 'spec/helpers/testing-library';
import Divider from './Divider';
describe('Divider', () => {
const props = {

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